editor_tests.rs

   1use super::*;
   2use crate::{
   3    scroll::scroll_amount::ScrollAmount,
   4    test::{
   5        assert_text_with_selections, build_editor, editor_lsp_test_context::EditorLspTestContext,
   6        editor_test_context::EditorTestContext, select_ranges,
   7    },
   8    JoinLines,
   9};
  10
  11use futures::StreamExt;
  12use gpui::{
  13    div,
  14    serde_json::{self, json},
  15    Div, Flatten, TestAppContext, VisualTestContext, WindowBounds, WindowOptions,
  16};
  17use indoc::indoc;
  18use language::{
  19    language_settings::{AllLanguageSettings, AllLanguageSettingsContent, LanguageSettingsContent},
  20    BracketPairConfig, FakeLspAdapter, LanguageConfig, LanguageConfigOverride, LanguageRegistry,
  21    Override, Point,
  22};
  23use parking_lot::Mutex;
  24use project::project_settings::{LspSettings, ProjectSettings};
  25use project::FakeFs;
  26use std::sync::atomic;
  27use std::sync::atomic::AtomicUsize;
  28use std::{cell::RefCell, future::Future, rc::Rc, time::Instant};
  29use unindent::Unindent;
  30use util::{
  31    assert_set_eq,
  32    http::FakeHttpClient,
  33    test::{marked_text_ranges, marked_text_ranges_by, sample_text, TextRangeMarker},
  34};
  35use workspace::{
  36    item::{FollowEvent, FollowableItem, Item, ItemHandle},
  37    NavigationEntry, ViewId,
  38};
  39
  40#[gpui::test]
  41fn test_edit_events(cx: &mut TestAppContext) {
  42    init_test(cx, |_| {});
  43
  44    let buffer = cx.build_model(|cx| {
  45        let mut buffer = language::Buffer::new(0, cx.entity_id().as_u64(), "123456");
  46        buffer.set_group_interval(Duration::from_secs(1));
  47        buffer
  48    });
  49
  50    let events = Rc::new(RefCell::new(Vec::new()));
  51    let editor1 = cx.add_window({
  52        let events = events.clone();
  53        |cx| {
  54            let view = cx.view().clone();
  55            cx.subscribe(&view, move |_, _, event: &EditorEvent, _| {
  56                if matches!(event, EditorEvent::Edited | EditorEvent::BufferEdited) {
  57                    events.borrow_mut().push(("editor1", event.clone()));
  58                }
  59            })
  60            .detach();
  61            Editor::for_buffer(buffer.clone(), None, cx)
  62        }
  63    });
  64
  65    let editor2 = cx.add_window({
  66        let events = events.clone();
  67        |cx| {
  68            cx.subscribe(&cx.view().clone(), move |_, _, event: &EditorEvent, _| {
  69                if matches!(event, EditorEvent::Edited | EditorEvent::BufferEdited) {
  70                    events.borrow_mut().push(("editor2", event.clone()));
  71                }
  72            })
  73            .detach();
  74            Editor::for_buffer(buffer.clone(), None, cx)
  75        }
  76    });
  77
  78    assert_eq!(mem::take(&mut *events.borrow_mut()), []);
  79
  80    // Mutating editor 1 will emit an `Edited` event only for that editor.
  81    editor1.update(cx, |editor, cx| editor.insert("X", cx));
  82    assert_eq!(
  83        mem::take(&mut *events.borrow_mut()),
  84        [
  85            ("editor1", EditorEvent::Edited),
  86            ("editor1", EditorEvent::BufferEdited),
  87            ("editor2", EditorEvent::BufferEdited),
  88        ]
  89    );
  90
  91    // Mutating editor 2 will emit an `Edited` event only for that editor.
  92    editor2.update(cx, |editor, cx| editor.delete(&Delete, cx));
  93    assert_eq!(
  94        mem::take(&mut *events.borrow_mut()),
  95        [
  96            ("editor2", EditorEvent::Edited),
  97            ("editor1", EditorEvent::BufferEdited),
  98            ("editor2", EditorEvent::BufferEdited),
  99        ]
 100    );
 101
 102    // Undoing on editor 1 will emit an `Edited` event only for that editor.
 103    editor1.update(cx, |editor, cx| editor.undo(&Undo, cx));
 104    assert_eq!(
 105        mem::take(&mut *events.borrow_mut()),
 106        [
 107            ("editor1", EditorEvent::Edited),
 108            ("editor1", EditorEvent::BufferEdited),
 109            ("editor2", EditorEvent::BufferEdited),
 110        ]
 111    );
 112
 113    // Redoing on editor 1 will emit an `Edited` event only for that editor.
 114    editor1.update(cx, |editor, cx| editor.redo(&Redo, cx));
 115    assert_eq!(
 116        mem::take(&mut *events.borrow_mut()),
 117        [
 118            ("editor1", EditorEvent::Edited),
 119            ("editor1", EditorEvent::BufferEdited),
 120            ("editor2", EditorEvent::BufferEdited),
 121        ]
 122    );
 123
 124    // Undoing on editor 2 will emit an `Edited` event only for that editor.
 125    editor2.update(cx, |editor, cx| editor.undo(&Undo, cx));
 126    assert_eq!(
 127        mem::take(&mut *events.borrow_mut()),
 128        [
 129            ("editor2", EditorEvent::Edited),
 130            ("editor1", EditorEvent::BufferEdited),
 131            ("editor2", EditorEvent::BufferEdited),
 132        ]
 133    );
 134
 135    // Redoing on editor 2 will emit an `Edited` event only for that editor.
 136    editor2.update(cx, |editor, cx| editor.redo(&Redo, cx));
 137    assert_eq!(
 138        mem::take(&mut *events.borrow_mut()),
 139        [
 140            ("editor2", EditorEvent::Edited),
 141            ("editor1", EditorEvent::BufferEdited),
 142            ("editor2", EditorEvent::BufferEdited),
 143        ]
 144    );
 145
 146    // No event is emitted when the mutation is a no-op.
 147    editor2.update(cx, |editor, cx| {
 148        editor.change_selections(None, cx, |s| s.select_ranges([0..0]));
 149
 150        editor.backspace(&Backspace, cx);
 151    });
 152    assert_eq!(mem::take(&mut *events.borrow_mut()), []);
 153}
 154
 155#[gpui::test]
 156fn test_undo_redo_with_selection_restoration(cx: &mut TestAppContext) {
 157    init_test(cx, |_| {});
 158
 159    let mut now = Instant::now();
 160    let buffer = cx.build_model(|cx| language::Buffer::new(0, cx.entity_id().as_u64(), "123456"));
 161    let group_interval = buffer.update(cx, |buffer, _| buffer.transaction_group_interval());
 162    let buffer = cx.build_model(|cx| MultiBuffer::singleton(buffer, cx));
 163    let editor = cx.add_window(|cx| build_editor(buffer.clone(), cx));
 164
 165    editor.update(cx, |editor, cx| {
 166        editor.start_transaction_at(now, cx);
 167        editor.change_selections(None, cx, |s| s.select_ranges([2..4]));
 168
 169        editor.insert("cd", cx);
 170        editor.end_transaction_at(now, cx);
 171        assert_eq!(editor.text(cx), "12cd56");
 172        assert_eq!(editor.selections.ranges(cx), vec![4..4]);
 173
 174        editor.start_transaction_at(now, cx);
 175        editor.change_selections(None, cx, |s| s.select_ranges([4..5]));
 176        editor.insert("e", cx);
 177        editor.end_transaction_at(now, cx);
 178        assert_eq!(editor.text(cx), "12cde6");
 179        assert_eq!(editor.selections.ranges(cx), vec![5..5]);
 180
 181        now += group_interval + Duration::from_millis(1);
 182        editor.change_selections(None, cx, |s| s.select_ranges([2..2]));
 183
 184        // Simulate an edit in another editor
 185        buffer.update(cx, |buffer, cx| {
 186            buffer.start_transaction_at(now, cx);
 187            buffer.edit([(0..1, "a")], None, cx);
 188            buffer.edit([(1..1, "b")], None, cx);
 189            buffer.end_transaction_at(now, cx);
 190        });
 191
 192        assert_eq!(editor.text(cx), "ab2cde6");
 193        assert_eq!(editor.selections.ranges(cx), vec![3..3]);
 194
 195        // Last transaction happened past the group interval in a different editor.
 196        // Undo it individually and don't restore selections.
 197        editor.undo(&Undo, cx);
 198        assert_eq!(editor.text(cx), "12cde6");
 199        assert_eq!(editor.selections.ranges(cx), vec![2..2]);
 200
 201        // First two transactions happened within the group interval in this editor.
 202        // Undo them together and restore selections.
 203        editor.undo(&Undo, cx);
 204        editor.undo(&Undo, cx); // Undo stack is empty here, so this is a no-op.
 205        assert_eq!(editor.text(cx), "123456");
 206        assert_eq!(editor.selections.ranges(cx), vec![0..0]);
 207
 208        // Redo the first two transactions together.
 209        editor.redo(&Redo, cx);
 210        assert_eq!(editor.text(cx), "12cde6");
 211        assert_eq!(editor.selections.ranges(cx), vec![5..5]);
 212
 213        // Redo the last transaction on its own.
 214        editor.redo(&Redo, cx);
 215        assert_eq!(editor.text(cx), "ab2cde6");
 216        assert_eq!(editor.selections.ranges(cx), vec![6..6]);
 217
 218        // Test empty transactions.
 219        editor.start_transaction_at(now, cx);
 220        editor.end_transaction_at(now, cx);
 221        editor.undo(&Undo, cx);
 222        assert_eq!(editor.text(cx), "12cde6");
 223    });
 224}
 225
 226#[gpui::test]
 227fn test_ime_composition(cx: &mut TestAppContext) {
 228    init_test(cx, |_| {});
 229
 230    let buffer = cx.build_model(|cx| {
 231        let mut buffer = language::Buffer::new(0, cx.entity_id().as_u64(), "abcde");
 232        // Ensure automatic grouping doesn't occur.
 233        buffer.set_group_interval(Duration::ZERO);
 234        buffer
 235    });
 236
 237    let buffer = cx.build_model(|cx| MultiBuffer::singleton(buffer, cx));
 238    cx.add_window(|cx| {
 239        let mut editor = build_editor(buffer.clone(), cx);
 240
 241        // Start a new IME composition.
 242        editor.replace_and_mark_text_in_range(Some(0..1), "à", None, cx);
 243        editor.replace_and_mark_text_in_range(Some(0..1), "á", None, cx);
 244        editor.replace_and_mark_text_in_range(Some(0..1), "ä", None, cx);
 245        assert_eq!(editor.text(cx), "äbcde");
 246        assert_eq!(
 247            editor.marked_text_ranges(cx),
 248            Some(vec![OffsetUtf16(0)..OffsetUtf16(1)])
 249        );
 250
 251        // Finalize IME composition.
 252        editor.replace_text_in_range(None, "ā", cx);
 253        assert_eq!(editor.text(cx), "ābcde");
 254        assert_eq!(editor.marked_text_ranges(cx), None);
 255
 256        // IME composition edits are grouped and are undone/redone at once.
 257        editor.undo(&Default::default(), cx);
 258        assert_eq!(editor.text(cx), "abcde");
 259        assert_eq!(editor.marked_text_ranges(cx), None);
 260        editor.redo(&Default::default(), cx);
 261        assert_eq!(editor.text(cx), "ābcde");
 262        assert_eq!(editor.marked_text_ranges(cx), None);
 263
 264        // Start a new IME composition.
 265        editor.replace_and_mark_text_in_range(Some(0..1), "à", None, cx);
 266        assert_eq!(
 267            editor.marked_text_ranges(cx),
 268            Some(vec![OffsetUtf16(0)..OffsetUtf16(1)])
 269        );
 270
 271        // Undoing during an IME composition cancels it.
 272        editor.undo(&Default::default(), cx);
 273        assert_eq!(editor.text(cx), "ābcde");
 274        assert_eq!(editor.marked_text_ranges(cx), None);
 275
 276        // Start a new IME composition with an invalid marked range, ensuring it gets clipped.
 277        editor.replace_and_mark_text_in_range(Some(4..999), "è", None, cx);
 278        assert_eq!(editor.text(cx), "ābcdè");
 279        assert_eq!(
 280            editor.marked_text_ranges(cx),
 281            Some(vec![OffsetUtf16(4)..OffsetUtf16(5)])
 282        );
 283
 284        // Finalize IME composition with an invalid replacement range, ensuring it gets clipped.
 285        editor.replace_text_in_range(Some(4..999), "ę", cx);
 286        assert_eq!(editor.text(cx), "ābcdę");
 287        assert_eq!(editor.marked_text_ranges(cx), None);
 288
 289        // Start a new IME composition with multiple cursors.
 290        editor.change_selections(None, cx, |s| {
 291            s.select_ranges([
 292                OffsetUtf16(1)..OffsetUtf16(1),
 293                OffsetUtf16(3)..OffsetUtf16(3),
 294                OffsetUtf16(5)..OffsetUtf16(5),
 295            ])
 296        });
 297        editor.replace_and_mark_text_in_range(Some(4..5), "XYZ", None, cx);
 298        assert_eq!(editor.text(cx), "XYZbXYZdXYZ");
 299        assert_eq!(
 300            editor.marked_text_ranges(cx),
 301            Some(vec![
 302                OffsetUtf16(0)..OffsetUtf16(3),
 303                OffsetUtf16(4)..OffsetUtf16(7),
 304                OffsetUtf16(8)..OffsetUtf16(11)
 305            ])
 306        );
 307
 308        // Ensure the newly-marked range gets treated as relative to the previously-marked ranges.
 309        editor.replace_and_mark_text_in_range(Some(1..2), "1", None, cx);
 310        assert_eq!(editor.text(cx), "X1ZbX1ZdX1Z");
 311        assert_eq!(
 312            editor.marked_text_ranges(cx),
 313            Some(vec![
 314                OffsetUtf16(1)..OffsetUtf16(2),
 315                OffsetUtf16(5)..OffsetUtf16(6),
 316                OffsetUtf16(9)..OffsetUtf16(10)
 317            ])
 318        );
 319
 320        // Finalize IME composition with multiple cursors.
 321        editor.replace_text_in_range(Some(9..10), "2", cx);
 322        assert_eq!(editor.text(cx), "X2ZbX2ZdX2Z");
 323        assert_eq!(editor.marked_text_ranges(cx), None);
 324
 325        editor
 326    });
 327}
 328
 329#[gpui::test]
 330fn test_selection_with_mouse(cx: &mut TestAppContext) {
 331    init_test(cx, |_| {});
 332
 333    let editor = cx.add_window(|cx| {
 334        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
 335        build_editor(buffer, cx)
 336    });
 337
 338    editor.update(cx, |view, cx| {
 339        view.begin_selection(DisplayPoint::new(2, 2), false, 1, cx);
 340    });
 341    assert_eq!(
 342        editor
 343            .update(cx, |view, cx| view.selections.display_ranges(cx))
 344            .unwrap(),
 345        [DisplayPoint::new(2, 2)..DisplayPoint::new(2, 2)]
 346    );
 347
 348    editor.update(cx, |view, cx| {
 349        view.update_selection(
 350            DisplayPoint::new(3, 3),
 351            0,
 352            gpui::Point::<f32>::default(),
 353            cx,
 354        );
 355    });
 356
 357    assert_eq!(
 358        editor
 359            .update(cx, |view, cx| view.selections.display_ranges(cx))
 360            .unwrap(),
 361        [DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)]
 362    );
 363
 364    editor.update(cx, |view, cx| {
 365        view.update_selection(
 366            DisplayPoint::new(1, 1),
 367            0,
 368            gpui::Point::<f32>::default(),
 369            cx,
 370        );
 371    });
 372
 373    assert_eq!(
 374        editor
 375            .update(cx, |view, cx| view.selections.display_ranges(cx))
 376            .unwrap(),
 377        [DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1)]
 378    );
 379
 380    editor.update(cx, |view, cx| {
 381        view.end_selection(cx);
 382        view.update_selection(
 383            DisplayPoint::new(3, 3),
 384            0,
 385            gpui::Point::<f32>::default(),
 386            cx,
 387        );
 388    });
 389
 390    assert_eq!(
 391        editor
 392            .update(cx, |view, cx| view.selections.display_ranges(cx))
 393            .unwrap(),
 394        [DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1)]
 395    );
 396
 397    editor.update(cx, |view, cx| {
 398        view.begin_selection(DisplayPoint::new(3, 3), true, 1, cx);
 399        view.update_selection(
 400            DisplayPoint::new(0, 0),
 401            0,
 402            gpui::Point::<f32>::default(),
 403            cx,
 404        );
 405    });
 406
 407    assert_eq!(
 408        editor
 409            .update(cx, |view, cx| view.selections.display_ranges(cx))
 410            .unwrap(),
 411        [
 412            DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1),
 413            DisplayPoint::new(3, 3)..DisplayPoint::new(0, 0)
 414        ]
 415    );
 416
 417    editor.update(cx, |view, cx| {
 418        view.end_selection(cx);
 419    });
 420
 421    assert_eq!(
 422        editor
 423            .update(cx, |view, cx| view.selections.display_ranges(cx))
 424            .unwrap(),
 425        [DisplayPoint::new(3, 3)..DisplayPoint::new(0, 0)]
 426    );
 427}
 428
 429#[gpui::test]
 430fn test_canceling_pending_selection(cx: &mut TestAppContext) {
 431    init_test(cx, |_| {});
 432
 433    let view = cx.add_window(|cx| {
 434        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
 435        build_editor(buffer, cx)
 436    });
 437
 438    view.update(cx, |view, cx| {
 439        view.begin_selection(DisplayPoint::new(2, 2), false, 1, cx);
 440        assert_eq!(
 441            view.selections.display_ranges(cx),
 442            [DisplayPoint::new(2, 2)..DisplayPoint::new(2, 2)]
 443        );
 444    });
 445
 446    view.update(cx, |view, cx| {
 447        view.update_selection(
 448            DisplayPoint::new(3, 3),
 449            0,
 450            gpui::Point::<f32>::default(),
 451            cx,
 452        );
 453        assert_eq!(
 454            view.selections.display_ranges(cx),
 455            [DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)]
 456        );
 457    });
 458
 459    view.update(cx, |view, cx| {
 460        view.cancel(&Cancel, cx);
 461        view.update_selection(
 462            DisplayPoint::new(1, 1),
 463            0,
 464            gpui::Point::<f32>::default(),
 465            cx,
 466        );
 467        assert_eq!(
 468            view.selections.display_ranges(cx),
 469            [DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)]
 470        );
 471    });
 472}
 473
 474#[gpui::test]
 475fn test_clone(cx: &mut TestAppContext) {
 476    init_test(cx, |_| {});
 477
 478    let (text, selection_ranges) = marked_text_ranges(
 479        indoc! {"
 480            one
 481            two
 482            threeˇ
 483            four
 484            fiveˇ
 485        "},
 486        true,
 487    );
 488
 489    let editor = cx.add_window(|cx| {
 490        let buffer = MultiBuffer::build_simple(&text, cx);
 491        build_editor(buffer, cx)
 492    });
 493
 494    editor.update(cx, |editor, cx| {
 495        editor.change_selections(None, cx, |s| s.select_ranges(selection_ranges.clone()));
 496        editor.fold_ranges(
 497            [
 498                Point::new(1, 0)..Point::new(2, 0),
 499                Point::new(3, 0)..Point::new(4, 0),
 500            ],
 501            true,
 502            cx,
 503        );
 504    });
 505
 506    let cloned_editor = editor
 507        .update(cx, |editor, cx| {
 508            cx.open_window(Default::default(), |cx| {
 509                cx.build_view(|cx| editor.clone(cx))
 510            })
 511        })
 512        .unwrap();
 513
 514    let snapshot = editor.update(cx, |e, cx| e.snapshot(cx)).unwrap();
 515    let cloned_snapshot = cloned_editor.update(cx, |e, cx| e.snapshot(cx)).unwrap();
 516
 517    assert_eq!(
 518        cloned_editor
 519            .update(cx, |e, cx| e.display_text(cx))
 520            .unwrap(),
 521        editor.update(cx, |e, cx| e.display_text(cx)).unwrap()
 522    );
 523    assert_eq!(
 524        cloned_snapshot
 525            .folds_in_range(0..text.len())
 526            .collect::<Vec<_>>(),
 527        snapshot.folds_in_range(0..text.len()).collect::<Vec<_>>(),
 528    );
 529    assert_set_eq!(
 530        cloned_editor
 531            .update(cx, |editor, cx| editor.selections.ranges::<Point>(cx))
 532            .unwrap(),
 533        editor
 534            .update(cx, |editor, cx| editor.selections.ranges(cx))
 535            .unwrap()
 536    );
 537    assert_set_eq!(
 538        cloned_editor
 539            .update(cx, |e, cx| e.selections.display_ranges(cx))
 540            .unwrap(),
 541        editor
 542            .update(cx, |e, cx| e.selections.display_ranges(cx))
 543            .unwrap()
 544    );
 545}
 546
 547//todo!(editor navigate)
 548#[gpui::test]
 549async fn test_navigation_history(cx: &mut TestAppContext) {
 550    init_test(cx, |_| {});
 551
 552    use workspace::item::Item;
 553
 554    let fs = FakeFs::new(cx.executor());
 555    let project = Project::test(fs, [], cx).await;
 556    let workspace = cx.add_window(|cx| Workspace::test_new(project, cx));
 557    let pane = workspace
 558        .update(cx, |workspace, _| workspace.active_pane().clone())
 559        .unwrap();
 560
 561    workspace.update(cx, |v, cx| {
 562        cx.build_view(|cx| {
 563            let buffer = MultiBuffer::build_simple(&sample_text(300, 5, 'a'), cx);
 564            let mut editor = build_editor(buffer.clone(), cx);
 565            let handle = cx.view();
 566            editor.set_nav_history(Some(pane.read(cx).nav_history_for_item(&handle)));
 567
 568            fn pop_history(editor: &mut Editor, cx: &mut WindowContext) -> Option<NavigationEntry> {
 569                editor.nav_history.as_mut().unwrap().pop_backward(cx)
 570            }
 571
 572            // Move the cursor a small distance.
 573            // Nothing is added to the navigation history.
 574            editor.change_selections(None, cx, |s| {
 575                s.select_display_ranges([DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)])
 576            });
 577            editor.change_selections(None, cx, |s| {
 578                s.select_display_ranges([DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0)])
 579            });
 580            assert!(pop_history(&mut editor, cx).is_none());
 581
 582            // Move the cursor a large distance.
 583            // The history can jump back to the previous position.
 584            editor.change_selections(None, cx, |s| {
 585                s.select_display_ranges([DisplayPoint::new(13, 0)..DisplayPoint::new(13, 3)])
 586            });
 587            let nav_entry = pop_history(&mut editor, cx).unwrap();
 588            editor.navigate(nav_entry.data.unwrap(), cx);
 589            assert_eq!(nav_entry.item.id(), cx.entity_id());
 590            assert_eq!(
 591                editor.selections.display_ranges(cx),
 592                &[DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0)]
 593            );
 594            assert!(pop_history(&mut editor, cx).is_none());
 595
 596            // Move the cursor a small distance via the mouse.
 597            // Nothing is added to the navigation history.
 598            editor.begin_selection(DisplayPoint::new(5, 0), false, 1, cx);
 599            editor.end_selection(cx);
 600            assert_eq!(
 601                editor.selections.display_ranges(cx),
 602                &[DisplayPoint::new(5, 0)..DisplayPoint::new(5, 0)]
 603            );
 604            assert!(pop_history(&mut editor, cx).is_none());
 605
 606            // Move the cursor a large distance via the mouse.
 607            // The history can jump back to the previous position.
 608            editor.begin_selection(DisplayPoint::new(15, 0), false, 1, cx);
 609            editor.end_selection(cx);
 610            assert_eq!(
 611                editor.selections.display_ranges(cx),
 612                &[DisplayPoint::new(15, 0)..DisplayPoint::new(15, 0)]
 613            );
 614            let nav_entry = pop_history(&mut editor, cx).unwrap();
 615            editor.navigate(nav_entry.data.unwrap(), cx);
 616            assert_eq!(nav_entry.item.id(), cx.entity_id());
 617            assert_eq!(
 618                editor.selections.display_ranges(cx),
 619                &[DisplayPoint::new(5, 0)..DisplayPoint::new(5, 0)]
 620            );
 621            assert!(pop_history(&mut editor, cx).is_none());
 622
 623            // Set scroll position to check later
 624            editor.set_scroll_position(gpui::Point::<f32>::new(5.5, 5.5), cx);
 625            let original_scroll_position = editor.scroll_manager.anchor();
 626
 627            // Jump to the end of the document and adjust scroll
 628            editor.move_to_end(&MoveToEnd, cx);
 629            editor.set_scroll_position(gpui::Point::<f32>::new(-2.5, -0.5), cx);
 630            assert_ne!(editor.scroll_manager.anchor(), original_scroll_position);
 631
 632            let nav_entry = pop_history(&mut editor, cx).unwrap();
 633            editor.navigate(nav_entry.data.unwrap(), cx);
 634            assert_eq!(editor.scroll_manager.anchor(), original_scroll_position);
 635
 636            // Ensure we don't panic when navigation data contains invalid anchors *and* points.
 637            let mut invalid_anchor = editor.scroll_manager.anchor().anchor;
 638            invalid_anchor.text_anchor.buffer_id = Some(999);
 639            let invalid_point = Point::new(9999, 0);
 640            editor.navigate(
 641                Box::new(NavigationData {
 642                    cursor_anchor: invalid_anchor,
 643                    cursor_position: invalid_point,
 644                    scroll_anchor: ScrollAnchor {
 645                        anchor: invalid_anchor,
 646                        offset: Default::default(),
 647                    },
 648                    scroll_top_row: invalid_point.row,
 649                }),
 650                cx,
 651            );
 652            assert_eq!(
 653                editor.selections.display_ranges(cx),
 654                &[editor.max_point(cx)..editor.max_point(cx)]
 655            );
 656            assert_eq!(
 657                editor.scroll_position(cx),
 658                gpui::Point::new(0., editor.max_point(cx).row() as f32)
 659            );
 660
 661            editor
 662        })
 663    });
 664}
 665
 666#[gpui::test]
 667fn test_cancel(cx: &mut TestAppContext) {
 668    init_test(cx, |_| {});
 669
 670    let view = cx.add_window(|cx| {
 671        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
 672        build_editor(buffer, cx)
 673    });
 674
 675    view.update(cx, |view, cx| {
 676        view.begin_selection(DisplayPoint::new(3, 4), false, 1, cx);
 677        view.update_selection(
 678            DisplayPoint::new(1, 1),
 679            0,
 680            gpui::Point::<f32>::default(),
 681            cx,
 682        );
 683        view.end_selection(cx);
 684
 685        view.begin_selection(DisplayPoint::new(0, 1), true, 1, cx);
 686        view.update_selection(
 687            DisplayPoint::new(0, 3),
 688            0,
 689            gpui::Point::<f32>::default(),
 690            cx,
 691        );
 692        view.end_selection(cx);
 693        assert_eq!(
 694            view.selections.display_ranges(cx),
 695            [
 696                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
 697                DisplayPoint::new(3, 4)..DisplayPoint::new(1, 1),
 698            ]
 699        );
 700    });
 701
 702    view.update(cx, |view, cx| {
 703        view.cancel(&Cancel, cx);
 704        assert_eq!(
 705            view.selections.display_ranges(cx),
 706            [DisplayPoint::new(3, 4)..DisplayPoint::new(1, 1)]
 707        );
 708    });
 709
 710    view.update(cx, |view, cx| {
 711        view.cancel(&Cancel, cx);
 712        assert_eq!(
 713            view.selections.display_ranges(cx),
 714            [DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1)]
 715        );
 716    });
 717}
 718
 719#[gpui::test]
 720fn test_fold_action(cx: &mut TestAppContext) {
 721    init_test(cx, |_| {});
 722
 723    let view = cx.add_window(|cx| {
 724        let buffer = MultiBuffer::build_simple(
 725            &"
 726                impl Foo {
 727                    // Hello!
 728
 729                    fn a() {
 730                        1
 731                    }
 732
 733                    fn b() {
 734                        2
 735                    }
 736
 737                    fn c() {
 738                        3
 739                    }
 740                }
 741            "
 742            .unindent(),
 743            cx,
 744        );
 745        build_editor(buffer.clone(), cx)
 746    });
 747
 748    view.update(cx, |view, cx| {
 749        view.change_selections(None, cx, |s| {
 750            s.select_display_ranges([DisplayPoint::new(8, 0)..DisplayPoint::new(12, 0)]);
 751        });
 752        view.fold(&Fold, cx);
 753        assert_eq!(
 754            view.display_text(cx),
 755            "
 756                impl Foo {
 757                    // Hello!
 758
 759                    fn a() {
 760                        1
 761                    }
 762
 763                    fn b() {⋯
 764                    }
 765
 766                    fn c() {⋯
 767                    }
 768                }
 769            "
 770            .unindent(),
 771        );
 772
 773        view.fold(&Fold, cx);
 774        assert_eq!(
 775            view.display_text(cx),
 776            "
 777                impl Foo {⋯
 778                }
 779            "
 780            .unindent(),
 781        );
 782
 783        view.unfold_lines(&UnfoldLines, cx);
 784        assert_eq!(
 785            view.display_text(cx),
 786            "
 787                impl Foo {
 788                    // Hello!
 789
 790                    fn a() {
 791                        1
 792                    }
 793
 794                    fn b() {⋯
 795                    }
 796
 797                    fn c() {⋯
 798                    }
 799                }
 800            "
 801            .unindent(),
 802        );
 803
 804        view.unfold_lines(&UnfoldLines, cx);
 805        assert_eq!(view.display_text(cx), view.buffer.read(cx).read(cx).text());
 806    });
 807}
 808
 809#[gpui::test]
 810fn test_move_cursor(cx: &mut TestAppContext) {
 811    init_test(cx, |_| {});
 812
 813    let buffer = cx.update(|cx| MultiBuffer::build_simple(&sample_text(6, 6, 'a'), cx));
 814    let view = cx.add_window(|cx| build_editor(buffer.clone(), cx));
 815
 816    buffer.update(cx, |buffer, cx| {
 817        buffer.edit(
 818            vec![
 819                (Point::new(1, 0)..Point::new(1, 0), "\t"),
 820                (Point::new(1, 1)..Point::new(1, 1), "\t"),
 821            ],
 822            None,
 823            cx,
 824        );
 825    });
 826    view.update(cx, |view, cx| {
 827        assert_eq!(
 828            view.selections.display_ranges(cx),
 829            &[DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)]
 830        );
 831
 832        view.move_down(&MoveDown, cx);
 833        assert_eq!(
 834            view.selections.display_ranges(cx),
 835            &[DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)]
 836        );
 837
 838        view.move_right(&MoveRight, cx);
 839        assert_eq!(
 840            view.selections.display_ranges(cx),
 841            &[DisplayPoint::new(1, 4)..DisplayPoint::new(1, 4)]
 842        );
 843
 844        view.move_left(&MoveLeft, cx);
 845        assert_eq!(
 846            view.selections.display_ranges(cx),
 847            &[DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)]
 848        );
 849
 850        view.move_up(&MoveUp, cx);
 851        assert_eq!(
 852            view.selections.display_ranges(cx),
 853            &[DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)]
 854        );
 855
 856        view.move_to_end(&MoveToEnd, cx);
 857        assert_eq!(
 858            view.selections.display_ranges(cx),
 859            &[DisplayPoint::new(5, 6)..DisplayPoint::new(5, 6)]
 860        );
 861
 862        view.move_to_beginning(&MoveToBeginning, cx);
 863        assert_eq!(
 864            view.selections.display_ranges(cx),
 865            &[DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)]
 866        );
 867
 868        view.change_selections(None, cx, |s| {
 869            s.select_display_ranges([DisplayPoint::new(0, 1)..DisplayPoint::new(0, 2)]);
 870        });
 871        view.select_to_beginning(&SelectToBeginning, cx);
 872        assert_eq!(
 873            view.selections.display_ranges(cx),
 874            &[DisplayPoint::new(0, 1)..DisplayPoint::new(0, 0)]
 875        );
 876
 877        view.select_to_end(&SelectToEnd, cx);
 878        assert_eq!(
 879            view.selections.display_ranges(cx),
 880            &[DisplayPoint::new(0, 1)..DisplayPoint::new(5, 6)]
 881        );
 882    });
 883}
 884
 885#[gpui::test]
 886fn test_move_cursor_multibyte(cx: &mut TestAppContext) {
 887    init_test(cx, |_| {});
 888
 889    let view = cx.add_window(|cx| {
 890        let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcde\nαβγδε", cx);
 891        build_editor(buffer.clone(), cx)
 892    });
 893
 894    assert_eq!('ⓐ'.len_utf8(), 3);
 895    assert_eq!('α'.len_utf8(), 2);
 896
 897    view.update(cx, |view, cx| {
 898        view.fold_ranges(
 899            vec![
 900                Point::new(0, 6)..Point::new(0, 12),
 901                Point::new(1, 2)..Point::new(1, 4),
 902                Point::new(2, 4)..Point::new(2, 8),
 903            ],
 904            true,
 905            cx,
 906        );
 907        assert_eq!(view.display_text(cx), "ⓐⓑ⋯ⓔ\nab⋯e\nαβ⋯ε");
 908
 909        view.move_right(&MoveRight, cx);
 910        assert_eq!(
 911            view.selections.display_ranges(cx),
 912            &[empty_range(0, "".len())]
 913        );
 914        view.move_right(&MoveRight, cx);
 915        assert_eq!(
 916            view.selections.display_ranges(cx),
 917            &[empty_range(0, "ⓐⓑ".len())]
 918        );
 919        view.move_right(&MoveRight, cx);
 920        assert_eq!(
 921            view.selections.display_ranges(cx),
 922            &[empty_range(0, "ⓐⓑ⋯".len())]
 923        );
 924
 925        view.move_down(&MoveDown, cx);
 926        assert_eq!(
 927            view.selections.display_ranges(cx),
 928            &[empty_range(1, "ab⋯e".len())]
 929        );
 930        view.move_left(&MoveLeft, cx);
 931        assert_eq!(
 932            view.selections.display_ranges(cx),
 933            &[empty_range(1, "ab⋯".len())]
 934        );
 935        view.move_left(&MoveLeft, cx);
 936        assert_eq!(
 937            view.selections.display_ranges(cx),
 938            &[empty_range(1, "ab".len())]
 939        );
 940        view.move_left(&MoveLeft, cx);
 941        assert_eq!(
 942            view.selections.display_ranges(cx),
 943            &[empty_range(1, "a".len())]
 944        );
 945
 946        view.move_down(&MoveDown, cx);
 947        assert_eq!(
 948            view.selections.display_ranges(cx),
 949            &[empty_range(2, "α".len())]
 950        );
 951        view.move_right(&MoveRight, cx);
 952        assert_eq!(
 953            view.selections.display_ranges(cx),
 954            &[empty_range(2, "αβ".len())]
 955        );
 956        view.move_right(&MoveRight, cx);
 957        assert_eq!(
 958            view.selections.display_ranges(cx),
 959            &[empty_range(2, "αβ⋯".len())]
 960        );
 961        view.move_right(&MoveRight, cx);
 962        assert_eq!(
 963            view.selections.display_ranges(cx),
 964            &[empty_range(2, "αβ⋯ε".len())]
 965        );
 966
 967        view.move_up(&MoveUp, cx);
 968        assert_eq!(
 969            view.selections.display_ranges(cx),
 970            &[empty_range(1, "ab⋯e".len())]
 971        );
 972        view.move_down(&MoveDown, cx);
 973        assert_eq!(
 974            view.selections.display_ranges(cx),
 975            &[empty_range(2, "αβ⋯ε".len())]
 976        );
 977        view.move_up(&MoveUp, cx);
 978        assert_eq!(
 979            view.selections.display_ranges(cx),
 980            &[empty_range(1, "ab⋯e".len())]
 981        );
 982
 983        view.move_up(&MoveUp, cx);
 984        assert_eq!(
 985            view.selections.display_ranges(cx),
 986            &[empty_range(0, "ⓐⓑ".len())]
 987        );
 988        view.move_left(&MoveLeft, cx);
 989        assert_eq!(
 990            view.selections.display_ranges(cx),
 991            &[empty_range(0, "".len())]
 992        );
 993        view.move_left(&MoveLeft, cx);
 994        assert_eq!(
 995            view.selections.display_ranges(cx),
 996            &[empty_range(0, "".len())]
 997        );
 998    });
 999}
1000
1001//todo!(finish editor tests)
1002#[gpui::test]
1003fn test_move_cursor_different_line_lengths(cx: &mut TestAppContext) {
1004    init_test(cx, |_| {});
1005
1006    let view = cx.add_window(|cx| {
1007        let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcd\nαβγ\nabcd\nⓐⓑⓒⓓⓔ\n", cx);
1008        build_editor(buffer.clone(), cx)
1009    });
1010    view.update(cx, |view, cx| {
1011        view.change_selections(None, cx, |s| {
1012            s.select_display_ranges([empty_range(0, "ⓐⓑⓒⓓⓔ".len())]);
1013        });
1014        view.move_down(&MoveDown, cx);
1015        assert_eq!(
1016            view.selections.display_ranges(cx),
1017            &[empty_range(1, "abcd".len())]
1018        );
1019
1020        view.move_down(&MoveDown, cx);
1021        assert_eq!(
1022            view.selections.display_ranges(cx),
1023            &[empty_range(2, "αβγ".len())]
1024        );
1025
1026        view.move_down(&MoveDown, cx);
1027        assert_eq!(
1028            view.selections.display_ranges(cx),
1029            &[empty_range(3, "abcd".len())]
1030        );
1031
1032        view.move_down(&MoveDown, cx);
1033        assert_eq!(
1034            view.selections.display_ranges(cx),
1035            &[empty_range(4, "ⓐⓑⓒⓓⓔ".len())]
1036        );
1037
1038        view.move_up(&MoveUp, cx);
1039        assert_eq!(
1040            view.selections.display_ranges(cx),
1041            &[empty_range(3, "abcd".len())]
1042        );
1043
1044        view.move_up(&MoveUp, cx);
1045        assert_eq!(
1046            view.selections.display_ranges(cx),
1047            &[empty_range(2, "αβγ".len())]
1048        );
1049    });
1050}
1051
1052#[gpui::test]
1053fn test_beginning_end_of_line(cx: &mut TestAppContext) {
1054    init_test(cx, |_| {});
1055
1056    let view = cx.add_window(|cx| {
1057        let buffer = MultiBuffer::build_simple("abc\n  def", cx);
1058        build_editor(buffer, cx)
1059    });
1060    view.update(cx, |view, cx| {
1061        view.change_selections(None, cx, |s| {
1062            s.select_display_ranges([
1063                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
1064                DisplayPoint::new(1, 4)..DisplayPoint::new(1, 4),
1065            ]);
1066        });
1067    });
1068
1069    view.update(cx, |view, cx| {
1070        view.move_to_beginning_of_line(&MoveToBeginningOfLine, cx);
1071        assert_eq!(
1072            view.selections.display_ranges(cx),
1073            &[
1074                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
1075                DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
1076            ]
1077        );
1078    });
1079
1080    view.update(cx, |view, cx| {
1081        view.move_to_beginning_of_line(&MoveToBeginningOfLine, cx);
1082        assert_eq!(
1083            view.selections.display_ranges(cx),
1084            &[
1085                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
1086                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
1087            ]
1088        );
1089    });
1090
1091    view.update(cx, |view, cx| {
1092        view.move_to_beginning_of_line(&MoveToBeginningOfLine, cx);
1093        assert_eq!(
1094            view.selections.display_ranges(cx),
1095            &[
1096                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
1097                DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
1098            ]
1099        );
1100    });
1101
1102    view.update(cx, |view, cx| {
1103        view.move_to_end_of_line(&MoveToEndOfLine, cx);
1104        assert_eq!(
1105            view.selections.display_ranges(cx),
1106            &[
1107                DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
1108                DisplayPoint::new(1, 5)..DisplayPoint::new(1, 5),
1109            ]
1110        );
1111    });
1112
1113    // Moving to the end of line again is a no-op.
1114    view.update(cx, |view, cx| {
1115        view.move_to_end_of_line(&MoveToEndOfLine, cx);
1116        assert_eq!(
1117            view.selections.display_ranges(cx),
1118            &[
1119                DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
1120                DisplayPoint::new(1, 5)..DisplayPoint::new(1, 5),
1121            ]
1122        );
1123    });
1124
1125    view.update(cx, |view, cx| {
1126        view.move_left(&MoveLeft, cx);
1127        view.select_to_beginning_of_line(
1128            &SelectToBeginningOfLine {
1129                stop_at_soft_wraps: true,
1130            },
1131            cx,
1132        );
1133        assert_eq!(
1134            view.selections.display_ranges(cx),
1135            &[
1136                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 0),
1137                DisplayPoint::new(1, 4)..DisplayPoint::new(1, 2),
1138            ]
1139        );
1140    });
1141
1142    view.update(cx, |view, cx| {
1143        view.select_to_beginning_of_line(
1144            &SelectToBeginningOfLine {
1145                stop_at_soft_wraps: true,
1146            },
1147            cx,
1148        );
1149        assert_eq!(
1150            view.selections.display_ranges(cx),
1151            &[
1152                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 0),
1153                DisplayPoint::new(1, 4)..DisplayPoint::new(1, 0),
1154            ]
1155        );
1156    });
1157
1158    view.update(cx, |view, cx| {
1159        view.select_to_beginning_of_line(
1160            &SelectToBeginningOfLine {
1161                stop_at_soft_wraps: true,
1162            },
1163            cx,
1164        );
1165        assert_eq!(
1166            view.selections.display_ranges(cx),
1167            &[
1168                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 0),
1169                DisplayPoint::new(1, 4)..DisplayPoint::new(1, 2),
1170            ]
1171        );
1172    });
1173
1174    view.update(cx, |view, cx| {
1175        view.select_to_end_of_line(
1176            &SelectToEndOfLine {
1177                stop_at_soft_wraps: true,
1178            },
1179            cx,
1180        );
1181        assert_eq!(
1182            view.selections.display_ranges(cx),
1183            &[
1184                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 3),
1185                DisplayPoint::new(1, 4)..DisplayPoint::new(1, 5),
1186            ]
1187        );
1188    });
1189
1190    view.update(cx, |view, cx| {
1191        view.delete_to_end_of_line(&DeleteToEndOfLine, cx);
1192        assert_eq!(view.display_text(cx), "ab\n  de");
1193        assert_eq!(
1194            view.selections.display_ranges(cx),
1195            &[
1196                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
1197                DisplayPoint::new(1, 4)..DisplayPoint::new(1, 4),
1198            ]
1199        );
1200    });
1201
1202    view.update(cx, |view, cx| {
1203        view.delete_to_beginning_of_line(&DeleteToBeginningOfLine, cx);
1204        assert_eq!(view.display_text(cx), "\n");
1205        assert_eq!(
1206            view.selections.display_ranges(cx),
1207            &[
1208                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
1209                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
1210            ]
1211        );
1212    });
1213}
1214
1215#[gpui::test]
1216fn test_prev_next_word_boundary(cx: &mut TestAppContext) {
1217    init_test(cx, |_| {});
1218
1219    let view = cx.add_window(|cx| {
1220        let buffer = MultiBuffer::build_simple("use std::str::{foo, bar}\n\n  {baz.qux()}", cx);
1221        build_editor(buffer, cx)
1222    });
1223    view.update(cx, |view, cx| {
1224        view.change_selections(None, cx, |s| {
1225            s.select_display_ranges([
1226                DisplayPoint::new(0, 11)..DisplayPoint::new(0, 11),
1227                DisplayPoint::new(2, 4)..DisplayPoint::new(2, 4),
1228            ])
1229        });
1230
1231        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
1232        assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n  {ˇbaz.qux()}", view, cx);
1233
1234        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
1235        assert_selection_ranges("use stdˇ::str::{foo, bar}\n\n  ˇ{baz.qux()}", view, cx);
1236
1237        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
1238        assert_selection_ranges("use ˇstd::str::{foo, bar}\n\nˇ  {baz.qux()}", view, cx);
1239
1240        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
1241        assert_selection_ranges("ˇuse std::str::{foo, bar}\nˇ\n  {baz.qux()}", view, cx);
1242
1243        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
1244        assert_selection_ranges("ˇuse std::str::{foo, barˇ}\n\n  {baz.qux()}", view, cx);
1245
1246        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
1247        assert_selection_ranges("useˇ std::str::{foo, bar}ˇ\n\n  {baz.qux()}", view, cx);
1248
1249        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
1250        assert_selection_ranges("use stdˇ::str::{foo, bar}\nˇ\n  {baz.qux()}", view, cx);
1251
1252        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
1253        assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n  {ˇbaz.qux()}", view, cx);
1254
1255        view.move_right(&MoveRight, cx);
1256        view.select_to_previous_word_start(&SelectToPreviousWordStart, cx);
1257        assert_selection_ranges("use std::«ˇs»tr::{foo, bar}\n\n  {«ˇb»az.qux()}", view, cx);
1258
1259        view.select_to_previous_word_start(&SelectToPreviousWordStart, cx);
1260        assert_selection_ranges("use std«ˇ::s»tr::{foo, bar}\n\n  «ˇ{b»az.qux()}", view, cx);
1261
1262        view.select_to_next_word_end(&SelectToNextWordEnd, cx);
1263        assert_selection_ranges("use std::«ˇs»tr::{foo, bar}\n\n  {«ˇb»az.qux()}", view, cx);
1264    });
1265}
1266
1267//todo!(finish editor tests)
1268#[gpui::test]
1269fn test_prev_next_word_bounds_with_soft_wrap(cx: &mut TestAppContext) {
1270    init_test(cx, |_| {});
1271
1272    let view = cx.add_window(|cx| {
1273        let buffer = MultiBuffer::build_simple("use one::{\n    two::three::four::five\n};", cx);
1274        build_editor(buffer, cx)
1275    });
1276
1277    view.update(cx, |view, cx| {
1278        view.set_wrap_width(Some(140.0.into()), cx);
1279        assert_eq!(
1280            view.display_text(cx),
1281            "use one::{\n    two::three::\n    four::five\n};"
1282        );
1283
1284        view.change_selections(None, cx, |s| {
1285            s.select_display_ranges([DisplayPoint::new(1, 7)..DisplayPoint::new(1, 7)]);
1286        });
1287
1288        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
1289        assert_eq!(
1290            view.selections.display_ranges(cx),
1291            &[DisplayPoint::new(1, 9)..DisplayPoint::new(1, 9)]
1292        );
1293
1294        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
1295        assert_eq!(
1296            view.selections.display_ranges(cx),
1297            &[DisplayPoint::new(1, 14)..DisplayPoint::new(1, 14)]
1298        );
1299
1300        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
1301        assert_eq!(
1302            view.selections.display_ranges(cx),
1303            &[DisplayPoint::new(2, 4)..DisplayPoint::new(2, 4)]
1304        );
1305
1306        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
1307        assert_eq!(
1308            view.selections.display_ranges(cx),
1309            &[DisplayPoint::new(2, 8)..DisplayPoint::new(2, 8)]
1310        );
1311
1312        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
1313        assert_eq!(
1314            view.selections.display_ranges(cx),
1315            &[DisplayPoint::new(2, 4)..DisplayPoint::new(2, 4)]
1316        );
1317
1318        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
1319        assert_eq!(
1320            view.selections.display_ranges(cx),
1321            &[DisplayPoint::new(1, 14)..DisplayPoint::new(1, 14)]
1322        );
1323    });
1324}
1325
1326//todo!(simulate_resize)
1327#[gpui::test]
1328async fn test_move_start_of_paragraph_end_of_paragraph(cx: &mut gpui::TestAppContext) {
1329    init_test(cx, |_| {});
1330    let mut cx = EditorTestContext::new(cx).await;
1331
1332    let line_height = cx.editor(|editor, cx| {
1333        editor
1334            .style()
1335            .unwrap()
1336            .text
1337            .line_height_in_pixels(cx.rem_size())
1338    });
1339    cx.simulate_window_resize(cx.window, size(px(100.), 4. * line_height));
1340
1341    cx.set_state(
1342        &r#"ˇone
1343        two
1344
1345        three
1346        fourˇ
1347        five
1348
1349        six"#
1350            .unindent(),
1351    );
1352
1353    cx.update_editor(|editor, cx| editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, cx));
1354    cx.assert_editor_state(
1355        &r#"one
1356        two
1357        ˇ
1358        three
1359        four
1360        five
1361        ˇ
1362        six"#
1363            .unindent(),
1364    );
1365
1366    cx.update_editor(|editor, cx| editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, cx));
1367    cx.assert_editor_state(
1368        &r#"one
1369        two
1370
1371        three
1372        four
1373        five
1374        ˇ
1375        sixˇ"#
1376            .unindent(),
1377    );
1378
1379    cx.update_editor(|editor, cx| editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, cx));
1380    cx.assert_editor_state(
1381        &r#"one
1382        two
1383
1384        three
1385        four
1386        five
1387
1388        sixˇ"#
1389            .unindent(),
1390    );
1391
1392    cx.update_editor(|editor, cx| editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, cx));
1393    cx.assert_editor_state(
1394        &r#"one
1395        two
1396
1397        three
1398        four
1399        five
1400        ˇ
1401        six"#
1402            .unindent(),
1403    );
1404
1405    cx.update_editor(|editor, cx| editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, cx));
1406    cx.assert_editor_state(
1407        &r#"one
1408        two
1409        ˇ
1410        three
1411        four
1412        five
1413
1414        six"#
1415            .unindent(),
1416    );
1417
1418    cx.update_editor(|editor, cx| editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, cx));
1419    cx.assert_editor_state(
1420        &r#"ˇone
1421        two
1422
1423        three
1424        four
1425        five
1426
1427        six"#
1428            .unindent(),
1429    );
1430}
1431
1432#[gpui::test]
1433async fn test_scroll_page_up_page_down(cx: &mut gpui::TestAppContext) {
1434    init_test(cx, |_| {});
1435    let mut cx = EditorTestContext::new(cx).await;
1436    let line_height = cx.editor(|editor, cx| {
1437        editor
1438            .style()
1439            .unwrap()
1440            .text
1441            .line_height_in_pixels(cx.rem_size())
1442    });
1443    let window = cx.window;
1444    cx.simulate_window_resize(window, size(px(1000.), 4. * line_height + px(0.5)));
1445
1446    cx.set_state(
1447        &r#"ˇone
1448        two
1449        three
1450        four
1451        five
1452        six
1453        seven
1454        eight
1455        nine
1456        ten
1457        "#,
1458    );
1459
1460    cx.update_editor(|editor, cx| {
1461        assert_eq!(
1462            editor.snapshot(cx).scroll_position(),
1463            gpui::Point::new(0., 0.)
1464        );
1465        editor.scroll_screen(&ScrollAmount::Page(1.), cx);
1466        assert_eq!(
1467            editor.snapshot(cx).scroll_position(),
1468            gpui::Point::new(0., 3.)
1469        );
1470        editor.scroll_screen(&ScrollAmount::Page(1.), cx);
1471        assert_eq!(
1472            editor.snapshot(cx).scroll_position(),
1473            gpui::Point::new(0., 6.)
1474        );
1475        editor.scroll_screen(&ScrollAmount::Page(-1.), cx);
1476        assert_eq!(
1477            editor.snapshot(cx).scroll_position(),
1478            gpui::Point::new(0., 3.)
1479        );
1480
1481        editor.scroll_screen(&ScrollAmount::Page(-0.5), cx);
1482        assert_eq!(
1483            editor.snapshot(cx).scroll_position(),
1484            gpui::Point::new(0., 1.)
1485        );
1486        editor.scroll_screen(&ScrollAmount::Page(0.5), cx);
1487        assert_eq!(
1488            editor.snapshot(cx).scroll_position(),
1489            gpui::Point::new(0., 3.)
1490        );
1491    });
1492}
1493
1494#[gpui::test]
1495async fn test_autoscroll(cx: &mut gpui::TestAppContext) {
1496    init_test(cx, |_| {});
1497    let mut cx = EditorTestContext::new(cx).await;
1498
1499    let line_height = cx.update_editor(|editor, cx| {
1500        editor.set_vertical_scroll_margin(2, cx);
1501        editor
1502            .style()
1503            .unwrap()
1504            .text
1505            .line_height_in_pixels(cx.rem_size())
1506    });
1507    let window = cx.window;
1508    cx.simulate_window_resize(window, size(px(1000.), 6. * line_height));
1509
1510    cx.set_state(
1511        &r#"ˇone
1512            two
1513            three
1514            four
1515            five
1516            six
1517            seven
1518            eight
1519            nine
1520            ten
1521        "#,
1522    );
1523    cx.update_editor(|editor, cx| {
1524        assert_eq!(
1525            editor.snapshot(cx).scroll_position(),
1526            gpui::Point::new(0., 0.0)
1527        );
1528    });
1529
1530    // Add a cursor below the visible area. Since both cursors cannot fit
1531    // on screen, the editor autoscrolls to reveal the newest cursor, and
1532    // allows the vertical scroll margin below that cursor.
1533    cx.update_editor(|editor, cx| {
1534        editor.change_selections(Some(Autoscroll::fit()), cx, |selections| {
1535            selections.select_ranges([
1536                Point::new(0, 0)..Point::new(0, 0),
1537                Point::new(6, 0)..Point::new(6, 0),
1538            ]);
1539        })
1540    });
1541    cx.update_editor(|editor, cx| {
1542        assert_eq!(
1543            editor.snapshot(cx).scroll_position(),
1544            gpui::Point::new(0., 3.0)
1545        );
1546    });
1547
1548    // Move down. The editor cursor scrolls down to track the newest cursor.
1549    cx.update_editor(|editor, cx| {
1550        editor.move_down(&Default::default(), cx);
1551    });
1552    cx.update_editor(|editor, cx| {
1553        assert_eq!(
1554            editor.snapshot(cx).scroll_position(),
1555            gpui::Point::new(0., 4.0)
1556        );
1557    });
1558
1559    // Add a cursor above the visible area. Since both cursors fit on screen,
1560    // the editor scrolls to show both.
1561    cx.update_editor(|editor, cx| {
1562        editor.change_selections(Some(Autoscroll::fit()), cx, |selections| {
1563            selections.select_ranges([
1564                Point::new(1, 0)..Point::new(1, 0),
1565                Point::new(6, 0)..Point::new(6, 0),
1566            ]);
1567        })
1568    });
1569    cx.update_editor(|editor, cx| {
1570        assert_eq!(
1571            editor.snapshot(cx).scroll_position(),
1572            gpui::Point::new(0., 1.0)
1573        );
1574    });
1575}
1576
1577#[gpui::test]
1578async fn test_move_page_up_page_down(cx: &mut gpui::TestAppContext) {
1579    init_test(cx, |_| {});
1580    let mut cx = EditorTestContext::new(cx).await;
1581
1582    let line_height = cx.editor(|editor, cx| {
1583        editor
1584            .style()
1585            .unwrap()
1586            .text
1587            .line_height_in_pixels(cx.rem_size())
1588    });
1589    let window = cx.window;
1590    cx.simulate_window_resize(window, size(px(100.), 4. * line_height));
1591    cx.set_state(
1592        &r#"
1593        ˇone
1594        two
1595        threeˇ
1596        four
1597        five
1598        six
1599        seven
1600        eight
1601        nine
1602        ten
1603        "#
1604        .unindent(),
1605    );
1606
1607    cx.update_editor(|editor, cx| editor.move_page_down(&MovePageDown::default(), cx));
1608    cx.assert_editor_state(
1609        &r#"
1610        one
1611        two
1612        three
1613        ˇfour
1614        five
1615        sixˇ
1616        seven
1617        eight
1618        nine
1619        ten
1620        "#
1621        .unindent(),
1622    );
1623
1624    cx.update_editor(|editor, cx| editor.move_page_down(&MovePageDown::default(), cx));
1625    cx.assert_editor_state(
1626        &r#"
1627        one
1628        two
1629        three
1630        four
1631        five
1632        six
1633        ˇseven
1634        eight
1635        nineˇ
1636        ten
1637        "#
1638        .unindent(),
1639    );
1640
1641    cx.update_editor(|editor, cx| editor.move_page_up(&MovePageUp::default(), cx));
1642    cx.assert_editor_state(
1643        &r#"
1644        one
1645        two
1646        three
1647        ˇfour
1648        five
1649        sixˇ
1650        seven
1651        eight
1652        nine
1653        ten
1654        "#
1655        .unindent(),
1656    );
1657
1658    cx.update_editor(|editor, cx| editor.move_page_up(&MovePageUp::default(), cx));
1659    cx.assert_editor_state(
1660        &r#"
1661        ˇone
1662        two
1663        threeˇ
1664        four
1665        five
1666        six
1667        seven
1668        eight
1669        nine
1670        ten
1671        "#
1672        .unindent(),
1673    );
1674
1675    // Test select collapsing
1676    cx.update_editor(|editor, cx| {
1677        editor.move_page_down(&MovePageDown::default(), cx);
1678        editor.move_page_down(&MovePageDown::default(), cx);
1679        editor.move_page_down(&MovePageDown::default(), cx);
1680    });
1681    cx.assert_editor_state(
1682        &r#"
1683        one
1684        two
1685        three
1686        four
1687        five
1688        six
1689        seven
1690        eight
1691        nine
1692        ˇten
1693        ˇ"#
1694        .unindent(),
1695    );
1696}
1697
1698#[gpui::test]
1699async fn test_delete_to_beginning_of_line(cx: &mut gpui::TestAppContext) {
1700    init_test(cx, |_| {});
1701    let mut cx = EditorTestContext::new(cx).await;
1702    cx.set_state("one «two threeˇ» four");
1703    cx.update_editor(|editor, cx| {
1704        editor.delete_to_beginning_of_line(&DeleteToBeginningOfLine, cx);
1705        assert_eq!(editor.text(cx), " four");
1706    });
1707}
1708
1709#[gpui::test]
1710fn test_delete_to_word_boundary(cx: &mut TestAppContext) {
1711    init_test(cx, |_| {});
1712
1713    let view = cx.add_window(|cx| {
1714        let buffer = MultiBuffer::build_simple("one two three four", cx);
1715        build_editor(buffer.clone(), cx)
1716    });
1717
1718    view.update(cx, |view, cx| {
1719        view.change_selections(None, cx, |s| {
1720            s.select_display_ranges([
1721                // an empty selection - the preceding word fragment is deleted
1722                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
1723                // characters selected - they are deleted
1724                DisplayPoint::new(0, 9)..DisplayPoint::new(0, 12),
1725            ])
1726        });
1727        view.delete_to_previous_word_start(&DeleteToPreviousWordStart, cx);
1728        assert_eq!(view.buffer.read(cx).read(cx).text(), "e two te four");
1729    });
1730
1731    view.update(cx, |view, cx| {
1732        view.change_selections(None, cx, |s| {
1733            s.select_display_ranges([
1734                // an empty selection - the following word fragment is deleted
1735                DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
1736                // characters selected - they are deleted
1737                DisplayPoint::new(0, 9)..DisplayPoint::new(0, 10),
1738            ])
1739        });
1740        view.delete_to_next_word_end(&DeleteToNextWordEnd, cx);
1741        assert_eq!(view.buffer.read(cx).read(cx).text(), "e t te our");
1742    });
1743}
1744
1745#[gpui::test]
1746fn test_newline(cx: &mut TestAppContext) {
1747    init_test(cx, |_| {});
1748
1749    let view = cx.add_window(|cx| {
1750        let buffer = MultiBuffer::build_simple("aaaa\n    bbbb\n", cx);
1751        build_editor(buffer.clone(), cx)
1752    });
1753
1754    view.update(cx, |view, cx| {
1755        view.change_selections(None, cx, |s| {
1756            s.select_display_ranges([
1757                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
1758                DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
1759                DisplayPoint::new(1, 6)..DisplayPoint::new(1, 6),
1760            ])
1761        });
1762
1763        view.newline(&Newline, cx);
1764        assert_eq!(view.text(cx), "aa\naa\n  \n    bb\n    bb\n");
1765    });
1766}
1767
1768#[gpui::test]
1769fn test_newline_with_old_selections(cx: &mut TestAppContext) {
1770    init_test(cx, |_| {});
1771
1772    let editor = cx.add_window(|cx| {
1773        let buffer = MultiBuffer::build_simple(
1774            "
1775                a
1776                b(
1777                    X
1778                )
1779                c(
1780                    X
1781                )
1782            "
1783            .unindent()
1784            .as_str(),
1785            cx,
1786        );
1787        let mut editor = build_editor(buffer.clone(), cx);
1788        editor.change_selections(None, cx, |s| {
1789            s.select_ranges([
1790                Point::new(2, 4)..Point::new(2, 5),
1791                Point::new(5, 4)..Point::new(5, 5),
1792            ])
1793        });
1794        editor
1795    });
1796
1797    editor.update(cx, |editor, cx| {
1798        // Edit the buffer directly, deleting ranges surrounding the editor's selections
1799        editor.buffer.update(cx, |buffer, cx| {
1800            buffer.edit(
1801                [
1802                    (Point::new(1, 2)..Point::new(3, 0), ""),
1803                    (Point::new(4, 2)..Point::new(6, 0), ""),
1804                ],
1805                None,
1806                cx,
1807            );
1808            assert_eq!(
1809                buffer.read(cx).text(),
1810                "
1811                    a
1812                    b()
1813                    c()
1814                "
1815                .unindent()
1816            );
1817        });
1818        assert_eq!(
1819            editor.selections.ranges(cx),
1820            &[
1821                Point::new(1, 2)..Point::new(1, 2),
1822                Point::new(2, 2)..Point::new(2, 2),
1823            ],
1824        );
1825
1826        editor.newline(&Newline, cx);
1827        assert_eq!(
1828            editor.text(cx),
1829            "
1830                a
1831                b(
1832                )
1833                c(
1834                )
1835            "
1836            .unindent()
1837        );
1838
1839        // The selections are moved after the inserted newlines
1840        assert_eq!(
1841            editor.selections.ranges(cx),
1842            &[
1843                Point::new(2, 0)..Point::new(2, 0),
1844                Point::new(4, 0)..Point::new(4, 0),
1845            ],
1846        );
1847    });
1848}
1849
1850#[gpui::test]
1851async fn test_newline_above(cx: &mut gpui::TestAppContext) {
1852    init_test(cx, |settings| {
1853        settings.defaults.tab_size = NonZeroU32::new(4)
1854    });
1855
1856    let language = Arc::new(
1857        Language::new(
1858            LanguageConfig::default(),
1859            Some(tree_sitter_rust::language()),
1860        )
1861        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
1862        .unwrap(),
1863    );
1864
1865    let mut cx = EditorTestContext::new(cx).await;
1866    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
1867    cx.set_state(indoc! {"
1868        const a: ˇA = (
18691870                «const_functionˇ»(ˇ),
1871                so«mˇ»et«hˇ»ing_ˇelse,ˇ
18721873        ˇ);ˇ
1874    "});
1875
1876    cx.update_editor(|e, cx| e.newline_above(&NewlineAbove, cx));
1877    cx.assert_editor_state(indoc! {"
1878        ˇ
1879        const a: A = (
1880            ˇ
1881            (
1882                ˇ
1883                ˇ
1884                const_function(),
1885                ˇ
1886                ˇ
1887                ˇ
1888                ˇ
1889                something_else,
1890                ˇ
1891            )
1892            ˇ
1893            ˇ
1894        );
1895    "});
1896}
1897
1898#[gpui::test]
1899async fn test_newline_below(cx: &mut gpui::TestAppContext) {
1900    init_test(cx, |settings| {
1901        settings.defaults.tab_size = NonZeroU32::new(4)
1902    });
1903
1904    let language = Arc::new(
1905        Language::new(
1906            LanguageConfig::default(),
1907            Some(tree_sitter_rust::language()),
1908        )
1909        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
1910        .unwrap(),
1911    );
1912
1913    let mut cx = EditorTestContext::new(cx).await;
1914    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
1915    cx.set_state(indoc! {"
1916        const a: ˇA = (
19171918                «const_functionˇ»(ˇ),
1919                so«mˇ»et«hˇ»ing_ˇelse,ˇ
19201921        ˇ);ˇ
1922    "});
1923
1924    cx.update_editor(|e, cx| e.newline_below(&NewlineBelow, cx));
1925    cx.assert_editor_state(indoc! {"
1926        const a: A = (
1927            ˇ
1928            (
1929                ˇ
1930                const_function(),
1931                ˇ
1932                ˇ
1933                something_else,
1934                ˇ
1935                ˇ
1936                ˇ
1937                ˇ
1938            )
1939            ˇ
1940        );
1941        ˇ
1942        ˇ
1943    "});
1944}
1945
1946#[gpui::test]
1947async fn test_newline_comments(cx: &mut gpui::TestAppContext) {
1948    init_test(cx, |settings| {
1949        settings.defaults.tab_size = NonZeroU32::new(4)
1950    });
1951
1952    let language = Arc::new(Language::new(
1953        LanguageConfig {
1954            line_comment: Some("//".into()),
1955            ..LanguageConfig::default()
1956        },
1957        None,
1958    ));
1959    {
1960        let mut cx = EditorTestContext::new(cx).await;
1961        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
1962        cx.set_state(indoc! {"
1963        // Fooˇ
1964    "});
1965
1966        cx.update_editor(|e, cx| e.newline(&Newline, cx));
1967        cx.assert_editor_state(indoc! {"
1968        // Foo
1969        //ˇ
1970    "});
1971        // Ensure that if cursor is before the comment start, we do not actually insert a comment prefix.
1972        cx.set_state(indoc! {"
1973        ˇ// Foo
1974    "});
1975        cx.update_editor(|e, cx| e.newline(&Newline, cx));
1976        cx.assert_editor_state(indoc! {"
1977
1978        ˇ// Foo
1979    "});
1980    }
1981    // Ensure that comment continuations can be disabled.
1982    update_test_language_settings(cx, |settings| {
1983        settings.defaults.extend_comment_on_newline = Some(false);
1984    });
1985    let mut cx = EditorTestContext::new(cx).await;
1986    cx.set_state(indoc! {"
1987        // Fooˇ
1988    "});
1989    cx.update_editor(|e, cx| e.newline(&Newline, cx));
1990    cx.assert_editor_state(indoc! {"
1991        // Foo
1992        ˇ
1993    "});
1994}
1995
1996#[gpui::test]
1997fn test_insert_with_old_selections(cx: &mut TestAppContext) {
1998    init_test(cx, |_| {});
1999
2000    let editor = cx.add_window(|cx| {
2001        let buffer = MultiBuffer::build_simple("a( X ), b( Y ), c( Z )", cx);
2002        let mut editor = build_editor(buffer.clone(), cx);
2003        editor.change_selections(None, cx, |s| s.select_ranges([3..4, 11..12, 19..20]));
2004        editor
2005    });
2006
2007    editor.update(cx, |editor, cx| {
2008        // Edit the buffer directly, deleting ranges surrounding the editor's selections
2009        editor.buffer.update(cx, |buffer, cx| {
2010            buffer.edit([(2..5, ""), (10..13, ""), (18..21, "")], None, cx);
2011            assert_eq!(buffer.read(cx).text(), "a(), b(), c()".unindent());
2012        });
2013        assert_eq!(editor.selections.ranges(cx), &[2..2, 7..7, 12..12],);
2014
2015        editor.insert("Z", cx);
2016        assert_eq!(editor.text(cx), "a(Z), b(Z), c(Z)");
2017
2018        // The selections are moved after the inserted characters
2019        assert_eq!(editor.selections.ranges(cx), &[3..3, 9..9, 15..15],);
2020    });
2021}
2022
2023#[gpui::test]
2024async fn test_tab(cx: &mut gpui::TestAppContext) {
2025    init_test(cx, |settings| {
2026        settings.defaults.tab_size = NonZeroU32::new(3)
2027    });
2028
2029    let mut cx = EditorTestContext::new(cx).await;
2030    cx.set_state(indoc! {"
2031        ˇabˇc
2032        ˇ🏀ˇ🏀ˇefg
20332034    "});
2035    cx.update_editor(|e, cx| e.tab(&Tab, cx));
2036    cx.assert_editor_state(indoc! {"
2037           ˇab ˇc
2038           ˇ🏀  ˇ🏀  ˇefg
2039        d  ˇ
2040    "});
2041
2042    cx.set_state(indoc! {"
2043        a
2044        «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
2045    "});
2046    cx.update_editor(|e, cx| e.tab(&Tab, cx));
2047    cx.assert_editor_state(indoc! {"
2048        a
2049           «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
2050    "});
2051}
2052
2053#[gpui::test]
2054async fn test_tab_in_leading_whitespace_auto_indents_lines(cx: &mut gpui::TestAppContext) {
2055    init_test(cx, |_| {});
2056
2057    let mut cx = EditorTestContext::new(cx).await;
2058    let language = Arc::new(
2059        Language::new(
2060            LanguageConfig::default(),
2061            Some(tree_sitter_rust::language()),
2062        )
2063        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
2064        .unwrap(),
2065    );
2066    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
2067
2068    // cursors that are already at the suggested indent level insert
2069    // a soft tab. cursors that are to the left of the suggested indent
2070    // auto-indent their line.
2071    cx.set_state(indoc! {"
2072        ˇ
2073        const a: B = (
2074            c(
2075                d(
2076        ˇ
2077                )
2078        ˇ
2079        ˇ    )
2080        );
2081    "});
2082    cx.update_editor(|e, cx| e.tab(&Tab, cx));
2083    cx.assert_editor_state(indoc! {"
2084            ˇ
2085        const a: B = (
2086            c(
2087                d(
2088                    ˇ
2089                )
2090                ˇ
2091            ˇ)
2092        );
2093    "});
2094
2095    // handle auto-indent when there are multiple cursors on the same line
2096    cx.set_state(indoc! {"
2097        const a: B = (
2098            c(
2099        ˇ    ˇ
2100        ˇ    )
2101        );
2102    "});
2103    cx.update_editor(|e, cx| e.tab(&Tab, cx));
2104    cx.assert_editor_state(indoc! {"
2105        const a: B = (
2106            c(
2107                ˇ
2108            ˇ)
2109        );
2110    "});
2111}
2112
2113#[gpui::test]
2114async fn test_tab_with_mixed_whitespace(cx: &mut gpui::TestAppContext) {
2115    init_test(cx, |settings| {
2116        settings.defaults.tab_size = NonZeroU32::new(4)
2117    });
2118
2119    let language = Arc::new(
2120        Language::new(
2121            LanguageConfig::default(),
2122            Some(tree_sitter_rust::language()),
2123        )
2124        .with_indents_query(r#"(_ "{" "}" @end) @indent"#)
2125        .unwrap(),
2126    );
2127
2128    let mut cx = EditorTestContext::new(cx).await;
2129    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
2130    cx.set_state(indoc! {"
2131        fn a() {
2132            if b {
2133        \t ˇc
2134            }
2135        }
2136    "});
2137
2138    cx.update_editor(|e, cx| e.tab(&Tab, cx));
2139    cx.assert_editor_state(indoc! {"
2140        fn a() {
2141            if b {
2142                ˇc
2143            }
2144        }
2145    "});
2146}
2147
2148#[gpui::test]
2149async fn test_indent_outdent(cx: &mut gpui::TestAppContext) {
2150    init_test(cx, |settings| {
2151        settings.defaults.tab_size = NonZeroU32::new(4);
2152    });
2153
2154    let mut cx = EditorTestContext::new(cx).await;
2155
2156    cx.set_state(indoc! {"
2157          «oneˇ» «twoˇ»
2158        three
2159         four
2160    "});
2161    cx.update_editor(|e, cx| e.tab(&Tab, cx));
2162    cx.assert_editor_state(indoc! {"
2163            «oneˇ» «twoˇ»
2164        three
2165         four
2166    "});
2167
2168    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
2169    cx.assert_editor_state(indoc! {"
2170        «oneˇ» «twoˇ»
2171        three
2172         four
2173    "});
2174
2175    // select across line ending
2176    cx.set_state(indoc! {"
2177        one two
2178        t«hree
2179        ˇ» four
2180    "});
2181    cx.update_editor(|e, cx| e.tab(&Tab, cx));
2182    cx.assert_editor_state(indoc! {"
2183        one two
2184            t«hree
2185        ˇ» four
2186    "});
2187
2188    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
2189    cx.assert_editor_state(indoc! {"
2190        one two
2191        t«hree
2192        ˇ» four
2193    "});
2194
2195    // Ensure that indenting/outdenting works when the cursor is at column 0.
2196    cx.set_state(indoc! {"
2197        one two
2198        ˇthree
2199            four
2200    "});
2201    cx.update_editor(|e, cx| e.tab(&Tab, cx));
2202    cx.assert_editor_state(indoc! {"
2203        one two
2204            ˇthree
2205            four
2206    "});
2207
2208    cx.set_state(indoc! {"
2209        one two
2210        ˇ    three
2211            four
2212    "});
2213    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
2214    cx.assert_editor_state(indoc! {"
2215        one two
2216        ˇthree
2217            four
2218    "});
2219}
2220
2221#[gpui::test]
2222async fn test_indent_outdent_with_hard_tabs(cx: &mut gpui::TestAppContext) {
2223    init_test(cx, |settings| {
2224        settings.defaults.hard_tabs = Some(true);
2225    });
2226
2227    let mut cx = EditorTestContext::new(cx).await;
2228
2229    // select two ranges on one line
2230    cx.set_state(indoc! {"
2231        «oneˇ» «twoˇ»
2232        three
2233        four
2234    "});
2235    cx.update_editor(|e, cx| e.tab(&Tab, cx));
2236    cx.assert_editor_state(indoc! {"
2237        \t«oneˇ» «twoˇ»
2238        three
2239        four
2240    "});
2241    cx.update_editor(|e, cx| e.tab(&Tab, cx));
2242    cx.assert_editor_state(indoc! {"
2243        \t\t«oneˇ» «twoˇ»
2244        three
2245        four
2246    "});
2247    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
2248    cx.assert_editor_state(indoc! {"
2249        \t«oneˇ» «twoˇ»
2250        three
2251        four
2252    "});
2253    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
2254    cx.assert_editor_state(indoc! {"
2255        «oneˇ» «twoˇ»
2256        three
2257        four
2258    "});
2259
2260    // select across a line ending
2261    cx.set_state(indoc! {"
2262        one two
2263        t«hree
2264        ˇ»four
2265    "});
2266    cx.update_editor(|e, cx| e.tab(&Tab, cx));
2267    cx.assert_editor_state(indoc! {"
2268        one two
2269        \tt«hree
2270        ˇ»four
2271    "});
2272    cx.update_editor(|e, cx| e.tab(&Tab, cx));
2273    cx.assert_editor_state(indoc! {"
2274        one two
2275        \t\tt«hree
2276        ˇ»four
2277    "});
2278    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
2279    cx.assert_editor_state(indoc! {"
2280        one two
2281        \tt«hree
2282        ˇ»four
2283    "});
2284    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
2285    cx.assert_editor_state(indoc! {"
2286        one two
2287        t«hree
2288        ˇ»four
2289    "});
2290
2291    // Ensure that indenting/outdenting works when the cursor is at column 0.
2292    cx.set_state(indoc! {"
2293        one two
2294        ˇthree
2295        four
2296    "});
2297    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
2298    cx.assert_editor_state(indoc! {"
2299        one two
2300        ˇthree
2301        four
2302    "});
2303    cx.update_editor(|e, cx| e.tab(&Tab, cx));
2304    cx.assert_editor_state(indoc! {"
2305        one two
2306        \tˇthree
2307        four
2308    "});
2309    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
2310    cx.assert_editor_state(indoc! {"
2311        one two
2312        ˇthree
2313        four
2314    "});
2315}
2316
2317#[gpui::test]
2318fn test_indent_outdent_with_excerpts(cx: &mut TestAppContext) {
2319    init_test(cx, |settings| {
2320        settings.languages.extend([
2321            (
2322                "TOML".into(),
2323                LanguageSettingsContent {
2324                    tab_size: NonZeroU32::new(2),
2325                    ..Default::default()
2326                },
2327            ),
2328            (
2329                "Rust".into(),
2330                LanguageSettingsContent {
2331                    tab_size: NonZeroU32::new(4),
2332                    ..Default::default()
2333                },
2334            ),
2335        ]);
2336    });
2337
2338    let toml_language = Arc::new(Language::new(
2339        LanguageConfig {
2340            name: "TOML".into(),
2341            ..Default::default()
2342        },
2343        None,
2344    ));
2345    let rust_language = Arc::new(Language::new(
2346        LanguageConfig {
2347            name: "Rust".into(),
2348            ..Default::default()
2349        },
2350        None,
2351    ));
2352
2353    let toml_buffer = cx.build_model(|cx| {
2354        Buffer::new(0, cx.entity_id().as_u64(), "a = 1\nb = 2\n").with_language(toml_language, cx)
2355    });
2356    let rust_buffer = cx.build_model(|cx| {
2357        Buffer::new(0, cx.entity_id().as_u64(), "const c: usize = 3;\n")
2358            .with_language(rust_language, cx)
2359    });
2360    let multibuffer = cx.build_model(|cx| {
2361        let mut multibuffer = MultiBuffer::new(0);
2362        multibuffer.push_excerpts(
2363            toml_buffer.clone(),
2364            [ExcerptRange {
2365                context: Point::new(0, 0)..Point::new(2, 0),
2366                primary: None,
2367            }],
2368            cx,
2369        );
2370        multibuffer.push_excerpts(
2371            rust_buffer.clone(),
2372            [ExcerptRange {
2373                context: Point::new(0, 0)..Point::new(1, 0),
2374                primary: None,
2375            }],
2376            cx,
2377        );
2378        multibuffer
2379    });
2380
2381    cx.add_window(|cx| {
2382        let mut editor = build_editor(multibuffer, cx);
2383
2384        assert_eq!(
2385            editor.text(cx),
2386            indoc! {"
2387                a = 1
2388                b = 2
2389
2390                const c: usize = 3;
2391            "}
2392        );
2393
2394        select_ranges(
2395            &mut editor,
2396            indoc! {"
2397                «aˇ» = 1
2398                b = 2
2399
2400                «const c:ˇ» usize = 3;
2401            "},
2402            cx,
2403        );
2404
2405        editor.tab(&Tab, cx);
2406        assert_text_with_selections(
2407            &mut editor,
2408            indoc! {"
2409                  «aˇ» = 1
2410                b = 2
2411
2412                    «const c:ˇ» usize = 3;
2413            "},
2414            cx,
2415        );
2416        editor.tab_prev(&TabPrev, cx);
2417        assert_text_with_selections(
2418            &mut editor,
2419            indoc! {"
2420                «aˇ» = 1
2421                b = 2
2422
2423                «const c:ˇ» usize = 3;
2424            "},
2425            cx,
2426        );
2427
2428        editor
2429    });
2430}
2431
2432#[gpui::test]
2433async fn test_backspace(cx: &mut gpui::TestAppContext) {
2434    init_test(cx, |_| {});
2435
2436    let mut cx = EditorTestContext::new(cx).await;
2437
2438    // Basic backspace
2439    cx.set_state(indoc! {"
2440        onˇe two three
2441        fou«rˇ» five six
2442        seven «ˇeight nine
2443        »ten
2444    "});
2445    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
2446    cx.assert_editor_state(indoc! {"
2447        oˇe two three
2448        fouˇ five six
2449        seven ˇten
2450    "});
2451
2452    // Test backspace inside and around indents
2453    cx.set_state(indoc! {"
2454        zero
2455            ˇone
2456                ˇtwo
2457            ˇ ˇ ˇ  three
2458        ˇ  ˇ  four
2459    "});
2460    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
2461    cx.assert_editor_state(indoc! {"
2462        zero
2463        ˇone
2464            ˇtwo
2465        ˇ  threeˇ  four
2466    "});
2467
2468    // Test backspace with line_mode set to true
2469    cx.update_editor(|e, _| e.selections.line_mode = true);
2470    cx.set_state(indoc! {"
2471        The ˇquick ˇbrown
2472        fox jumps over
2473        the lazy dog
2474        ˇThe qu«ick bˇ»rown"});
2475    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
2476    cx.assert_editor_state(indoc! {"
2477        ˇfox jumps over
2478        the lazy dogˇ"});
2479}
2480
2481#[gpui::test]
2482async fn test_delete(cx: &mut gpui::TestAppContext) {
2483    init_test(cx, |_| {});
2484
2485    let mut cx = EditorTestContext::new(cx).await;
2486    cx.set_state(indoc! {"
2487        onˇe two three
2488        fou«rˇ» five six
2489        seven «ˇeight nine
2490        »ten
2491    "});
2492    cx.update_editor(|e, cx| e.delete(&Delete, cx));
2493    cx.assert_editor_state(indoc! {"
2494        onˇ two three
2495        fouˇ five six
2496        seven ˇten
2497    "});
2498
2499    // Test backspace with line_mode set to true
2500    cx.update_editor(|e, _| e.selections.line_mode = true);
2501    cx.set_state(indoc! {"
2502        The ˇquick ˇbrown
2503        fox «ˇjum»ps over
2504        the lazy dog
2505        ˇThe qu«ick bˇ»rown"});
2506    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
2507    cx.assert_editor_state("ˇthe lazy dogˇ");
2508}
2509
2510#[gpui::test]
2511fn test_delete_line(cx: &mut TestAppContext) {
2512    init_test(cx, |_| {});
2513
2514    let view = cx.add_window(|cx| {
2515        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
2516        build_editor(buffer, cx)
2517    });
2518    view.update(cx, |view, cx| {
2519        view.change_selections(None, cx, |s| {
2520            s.select_display_ranges([
2521                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
2522                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
2523                DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
2524            ])
2525        });
2526        view.delete_line(&DeleteLine, cx);
2527        assert_eq!(view.display_text(cx), "ghi");
2528        assert_eq!(
2529            view.selections.display_ranges(cx),
2530            vec![
2531                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
2532                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1)
2533            ]
2534        );
2535    });
2536
2537    let view = cx.add_window(|cx| {
2538        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
2539        build_editor(buffer, cx)
2540    });
2541    view.update(cx, |view, cx| {
2542        view.change_selections(None, cx, |s| {
2543            s.select_display_ranges([DisplayPoint::new(2, 0)..DisplayPoint::new(0, 1)])
2544        });
2545        view.delete_line(&DeleteLine, cx);
2546        assert_eq!(view.display_text(cx), "ghi\n");
2547        assert_eq!(
2548            view.selections.display_ranges(cx),
2549            vec![DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1)]
2550        );
2551    });
2552}
2553
2554//todo!(select_anchor_ranges)
2555#[gpui::test]
2556fn test_join_lines_with_single_selection(cx: &mut TestAppContext) {
2557    init_test(cx, |_| {});
2558
2559    cx.add_window(|cx| {
2560        let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
2561        let mut editor = build_editor(buffer.clone(), cx);
2562        let buffer = buffer.read(cx).as_singleton().unwrap();
2563
2564        assert_eq!(
2565            editor.selections.ranges::<Point>(cx),
2566            &[Point::new(0, 0)..Point::new(0, 0)]
2567        );
2568
2569        // When on single line, replace newline at end by space
2570        editor.join_lines(&JoinLines, cx);
2571        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
2572        assert_eq!(
2573            editor.selections.ranges::<Point>(cx),
2574            &[Point::new(0, 3)..Point::new(0, 3)]
2575        );
2576
2577        // When multiple lines are selected, remove newlines that are spanned by the selection
2578        editor.change_selections(None, cx, |s| {
2579            s.select_ranges([Point::new(0, 5)..Point::new(2, 2)])
2580        });
2581        editor.join_lines(&JoinLines, cx);
2582        assert_eq!(buffer.read(cx).text(), "aaa bbb ccc ddd\n\n");
2583        assert_eq!(
2584            editor.selections.ranges::<Point>(cx),
2585            &[Point::new(0, 11)..Point::new(0, 11)]
2586        );
2587
2588        // Undo should be transactional
2589        editor.undo(&Undo, cx);
2590        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
2591        assert_eq!(
2592            editor.selections.ranges::<Point>(cx),
2593            &[Point::new(0, 5)..Point::new(2, 2)]
2594        );
2595
2596        // When joining an empty line don't insert a space
2597        editor.change_selections(None, cx, |s| {
2598            s.select_ranges([Point::new(2, 1)..Point::new(2, 2)])
2599        });
2600        editor.join_lines(&JoinLines, cx);
2601        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n");
2602        assert_eq!(
2603            editor.selections.ranges::<Point>(cx),
2604            [Point::new(2, 3)..Point::new(2, 3)]
2605        );
2606
2607        // We can remove trailing newlines
2608        editor.join_lines(&JoinLines, cx);
2609        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
2610        assert_eq!(
2611            editor.selections.ranges::<Point>(cx),
2612            [Point::new(2, 3)..Point::new(2, 3)]
2613        );
2614
2615        // We don't blow up on the last line
2616        editor.join_lines(&JoinLines, cx);
2617        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
2618        assert_eq!(
2619            editor.selections.ranges::<Point>(cx),
2620            [Point::new(2, 3)..Point::new(2, 3)]
2621        );
2622
2623        // reset to test indentation
2624        editor.buffer.update(cx, |buffer, cx| {
2625            buffer.edit(
2626                [
2627                    (Point::new(1, 0)..Point::new(1, 2), "  "),
2628                    (Point::new(2, 0)..Point::new(2, 3), "  \n\td"),
2629                ],
2630                None,
2631                cx,
2632            )
2633        });
2634
2635        // We remove any leading spaces
2636        assert_eq!(buffer.read(cx).text(), "aaa bbb\n  c\n  \n\td");
2637        editor.change_selections(None, cx, |s| {
2638            s.select_ranges([Point::new(0, 1)..Point::new(0, 1)])
2639        });
2640        editor.join_lines(&JoinLines, cx);
2641        assert_eq!(buffer.read(cx).text(), "aaa bbb c\n  \n\td");
2642
2643        // We don't insert a space for a line containing only spaces
2644        editor.join_lines(&JoinLines, cx);
2645        assert_eq!(buffer.read(cx).text(), "aaa bbb c\n\td");
2646
2647        // We ignore any leading tabs
2648        editor.join_lines(&JoinLines, cx);
2649        assert_eq!(buffer.read(cx).text(), "aaa bbb c d");
2650
2651        editor
2652    });
2653}
2654
2655#[gpui::test]
2656fn test_join_lines_with_multi_selection(cx: &mut TestAppContext) {
2657    init_test(cx, |_| {});
2658
2659    cx.add_window(|cx| {
2660        let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
2661        let mut editor = build_editor(buffer.clone(), cx);
2662        let buffer = buffer.read(cx).as_singleton().unwrap();
2663
2664        editor.change_selections(None, cx, |s| {
2665            s.select_ranges([
2666                Point::new(0, 2)..Point::new(1, 1),
2667                Point::new(1, 2)..Point::new(1, 2),
2668                Point::new(3, 1)..Point::new(3, 2),
2669            ])
2670        });
2671
2672        editor.join_lines(&JoinLines, cx);
2673        assert_eq!(buffer.read(cx).text(), "aaa bbb ccc\nddd\n");
2674
2675        assert_eq!(
2676            editor.selections.ranges::<Point>(cx),
2677            [
2678                Point::new(0, 7)..Point::new(0, 7),
2679                Point::new(1, 3)..Point::new(1, 3)
2680            ]
2681        );
2682        editor
2683    });
2684}
2685
2686#[gpui::test]
2687async fn test_manipulate_lines_with_single_selection(cx: &mut TestAppContext) {
2688    init_test(cx, |_| {});
2689
2690    let mut cx = EditorTestContext::new(cx).await;
2691
2692    // Test sort_lines_case_insensitive()
2693    cx.set_state(indoc! {"
2694        «z
2695        y
2696        x
2697        Z
2698        Y
2699        Xˇ»
2700    "});
2701    cx.update_editor(|e, cx| e.sort_lines_case_insensitive(&SortLinesCaseInsensitive, cx));
2702    cx.assert_editor_state(indoc! {"
2703        «x
2704        X
2705        y
2706        Y
2707        z
2708        Zˇ»
2709    "});
2710
2711    // Test reverse_lines()
2712    cx.set_state(indoc! {"
2713        «5
2714        4
2715        3
2716        2
2717        1ˇ»
2718    "});
2719    cx.update_editor(|e, cx| e.reverse_lines(&ReverseLines, cx));
2720    cx.assert_editor_state(indoc! {"
2721        «1
2722        2
2723        3
2724        4
2725        5ˇ»
2726    "});
2727
2728    // Skip testing shuffle_line()
2729
2730    // From here on out, test more complex cases of manipulate_lines() with a single driver method: sort_lines_case_sensitive()
2731    // Since all methods calling manipulate_lines() are doing the exact same general thing (reordering lines)
2732
2733    // Don't manipulate when cursor is on single line, but expand the selection
2734    cx.set_state(indoc! {"
2735        ddˇdd
2736        ccc
2737        bb
2738        a
2739    "});
2740    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
2741    cx.assert_editor_state(indoc! {"
2742        «ddddˇ»
2743        ccc
2744        bb
2745        a
2746    "});
2747
2748    // Basic manipulate case
2749    // Start selection moves to column 0
2750    // End of selection shrinks to fit shorter line
2751    cx.set_state(indoc! {"
2752        dd«d
2753        ccc
2754        bb
2755        aaaaaˇ»
2756    "});
2757    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
2758    cx.assert_editor_state(indoc! {"
2759        «aaaaa
2760        bb
2761        ccc
2762        dddˇ»
2763    "});
2764
2765    // Manipulate case with newlines
2766    cx.set_state(indoc! {"
2767        dd«d
2768        ccc
2769
2770        bb
2771        aaaaa
2772
2773        ˇ»
2774    "});
2775    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
2776    cx.assert_editor_state(indoc! {"
2777        «
2778
2779        aaaaa
2780        bb
2781        ccc
2782        dddˇ»
2783
2784    "});
2785}
2786
2787#[gpui::test]
2788async fn test_manipulate_lines_with_multi_selection(cx: &mut TestAppContext) {
2789    init_test(cx, |_| {});
2790
2791    let mut cx = EditorTestContext::new(cx).await;
2792
2793    // Manipulate with multiple selections on a single line
2794    cx.set_state(indoc! {"
2795        dd«dd
2796        cˇ»c«c
2797        bb
2798        aaaˇ»aa
2799    "});
2800    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
2801    cx.assert_editor_state(indoc! {"
2802        «aaaaa
2803        bb
2804        ccc
2805        ddddˇ»
2806    "});
2807
2808    // Manipulate with multiple disjoin selections
2809    cx.set_state(indoc! {"
28102811        4
2812        3
2813        2
2814        1ˇ»
2815
2816        dd«dd
2817        ccc
2818        bb
2819        aaaˇ»aa
2820    "});
2821    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
2822    cx.assert_editor_state(indoc! {"
2823        «1
2824        2
2825        3
2826        4
2827        5ˇ»
2828
2829        «aaaaa
2830        bb
2831        ccc
2832        ddddˇ»
2833    "});
2834}
2835
2836#[gpui::test]
2837async fn test_manipulate_text(cx: &mut TestAppContext) {
2838    init_test(cx, |_| {});
2839
2840    let mut cx = EditorTestContext::new(cx).await;
2841
2842    // Test convert_to_upper_case()
2843    cx.set_state(indoc! {"
2844        «hello worldˇ»
2845    "});
2846    cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx));
2847    cx.assert_editor_state(indoc! {"
2848        «HELLO WORLDˇ»
2849    "});
2850
2851    // Test convert_to_lower_case()
2852    cx.set_state(indoc! {"
2853        «HELLO WORLDˇ»
2854    "});
2855    cx.update_editor(|e, cx| e.convert_to_lower_case(&ConvertToLowerCase, cx));
2856    cx.assert_editor_state(indoc! {"
2857        «hello worldˇ»
2858    "});
2859
2860    // Test multiple line, single selection case
2861    // Test code hack that covers the fact that to_case crate doesn't support '\n' as a word boundary
2862    cx.set_state(indoc! {"
2863        «The quick brown
2864        fox jumps over
2865        the lazy dogˇ»
2866    "});
2867    cx.update_editor(|e, cx| e.convert_to_title_case(&ConvertToTitleCase, cx));
2868    cx.assert_editor_state(indoc! {"
2869        «The Quick Brown
2870        Fox Jumps Over
2871        The Lazy Dogˇ»
2872    "});
2873
2874    // Test multiple line, single selection case
2875    // Test code hack that covers the fact that to_case crate doesn't support '\n' as a word boundary
2876    cx.set_state(indoc! {"
2877        «The quick brown
2878        fox jumps over
2879        the lazy dogˇ»
2880    "});
2881    cx.update_editor(|e, cx| e.convert_to_upper_camel_case(&ConvertToUpperCamelCase, cx));
2882    cx.assert_editor_state(indoc! {"
2883        «TheQuickBrown
2884        FoxJumpsOver
2885        TheLazyDogˇ»
2886    "});
2887
2888    // From here on out, test more complex cases of manipulate_text()
2889
2890    // Test no selection case - should affect words cursors are in
2891    // Cursor at beginning, middle, and end of word
2892    cx.set_state(indoc! {"
2893        ˇhello big beauˇtiful worldˇ
2894    "});
2895    cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx));
2896    cx.assert_editor_state(indoc! {"
2897        «HELLOˇ» big «BEAUTIFULˇ» «WORLDˇ»
2898    "});
2899
2900    // Test multiple selections on a single line and across multiple lines
2901    cx.set_state(indoc! {"
2902        «Theˇ» quick «brown
2903        foxˇ» jumps «overˇ»
2904        the «lazyˇ» dog
2905    "});
2906    cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx));
2907    cx.assert_editor_state(indoc! {"
2908        «THEˇ» quick «BROWN
2909        FOXˇ» jumps «OVERˇ»
2910        the «LAZYˇ» dog
2911    "});
2912
2913    // Test case where text length grows
2914    cx.set_state(indoc! {"
2915        «tschüߡ»
2916    "});
2917    cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx));
2918    cx.assert_editor_state(indoc! {"
2919        «TSCHÜSSˇ»
2920    "});
2921
2922    // Test to make sure we don't crash when text shrinks
2923    cx.set_state(indoc! {"
2924        aaa_bbbˇ
2925    "});
2926    cx.update_editor(|e, cx| e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, cx));
2927    cx.assert_editor_state(indoc! {"
2928        «aaaBbbˇ»
2929    "});
2930
2931    // Test to make sure we all aware of the fact that each word can grow and shrink
2932    // Final selections should be aware of this fact
2933    cx.set_state(indoc! {"
2934        aaa_bˇbb bbˇb_ccc ˇccc_ddd
2935    "});
2936    cx.update_editor(|e, cx| e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, cx));
2937    cx.assert_editor_state(indoc! {"
2938        «aaaBbbˇ» «bbbCccˇ» «cccDddˇ»
2939    "});
2940}
2941
2942#[gpui::test]
2943fn test_duplicate_line(cx: &mut TestAppContext) {
2944    init_test(cx, |_| {});
2945
2946    let view = cx.add_window(|cx| {
2947        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
2948        build_editor(buffer, cx)
2949    });
2950    view.update(cx, |view, cx| {
2951        view.change_selections(None, cx, |s| {
2952            s.select_display_ranges([
2953                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
2954                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
2955                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
2956                DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
2957            ])
2958        });
2959        view.duplicate_line(&DuplicateLine, cx);
2960        assert_eq!(view.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
2961        assert_eq!(
2962            view.selections.display_ranges(cx),
2963            vec![
2964                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
2965                DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
2966                DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
2967                DisplayPoint::new(6, 0)..DisplayPoint::new(6, 0),
2968            ]
2969        );
2970    });
2971
2972    let view = cx.add_window(|cx| {
2973        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
2974        build_editor(buffer, cx)
2975    });
2976    view.update(cx, |view, cx| {
2977        view.change_selections(None, cx, |s| {
2978            s.select_display_ranges([
2979                DisplayPoint::new(0, 1)..DisplayPoint::new(1, 1),
2980                DisplayPoint::new(1, 2)..DisplayPoint::new(2, 1),
2981            ])
2982        });
2983        view.duplicate_line(&DuplicateLine, cx);
2984        assert_eq!(view.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
2985        assert_eq!(
2986            view.selections.display_ranges(cx),
2987            vec![
2988                DisplayPoint::new(3, 1)..DisplayPoint::new(4, 1),
2989                DisplayPoint::new(4, 2)..DisplayPoint::new(5, 1),
2990            ]
2991        );
2992    });
2993}
2994
2995#[gpui::test]
2996fn test_move_line_up_down(cx: &mut TestAppContext) {
2997    init_test(cx, |_| {});
2998
2999    let view = cx.add_window(|cx| {
3000        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
3001        build_editor(buffer, cx)
3002    });
3003    view.update(cx, |view, cx| {
3004        view.fold_ranges(
3005            vec![
3006                Point::new(0, 2)..Point::new(1, 2),
3007                Point::new(2, 3)..Point::new(4, 1),
3008                Point::new(7, 0)..Point::new(8, 4),
3009            ],
3010            true,
3011            cx,
3012        );
3013        view.change_selections(None, cx, |s| {
3014            s.select_display_ranges([
3015                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
3016                DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1),
3017                DisplayPoint::new(3, 2)..DisplayPoint::new(4, 3),
3018                DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2),
3019            ])
3020        });
3021        assert_eq!(
3022            view.display_text(cx),
3023            "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i\njjjjj"
3024        );
3025
3026        view.move_line_up(&MoveLineUp, cx);
3027        assert_eq!(
3028            view.display_text(cx),
3029            "aa⋯bbb\nccc⋯eeee\nggggg\n⋯i\njjjjj\nfffff"
3030        );
3031        assert_eq!(
3032            view.selections.display_ranges(cx),
3033            vec![
3034                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
3035                DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
3036                DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3),
3037                DisplayPoint::new(4, 0)..DisplayPoint::new(4, 2)
3038            ]
3039        );
3040    });
3041
3042    view.update(cx, |view, cx| {
3043        view.move_line_down(&MoveLineDown, cx);
3044        assert_eq!(
3045            view.display_text(cx),
3046            "ccc⋯eeee\naa⋯bbb\nfffff\nggggg\n⋯i\njjjjj"
3047        );
3048        assert_eq!(
3049            view.selections.display_ranges(cx),
3050            vec![
3051                DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
3052                DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1),
3053                DisplayPoint::new(3, 2)..DisplayPoint::new(4, 3),
3054                DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2)
3055            ]
3056        );
3057    });
3058
3059    view.update(cx, |view, cx| {
3060        view.move_line_down(&MoveLineDown, cx);
3061        assert_eq!(
3062            view.display_text(cx),
3063            "ccc⋯eeee\nfffff\naa⋯bbb\nggggg\n⋯i\njjjjj"
3064        );
3065        assert_eq!(
3066            view.selections.display_ranges(cx),
3067            vec![
3068                DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
3069                DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1),
3070                DisplayPoint::new(3, 2)..DisplayPoint::new(4, 3),
3071                DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2)
3072            ]
3073        );
3074    });
3075
3076    view.update(cx, |view, cx| {
3077        view.move_line_up(&MoveLineUp, cx);
3078        assert_eq!(
3079            view.display_text(cx),
3080            "ccc⋯eeee\naa⋯bbb\nggggg\n⋯i\njjjjj\nfffff"
3081        );
3082        assert_eq!(
3083            view.selections.display_ranges(cx),
3084            vec![
3085                DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
3086                DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
3087                DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3),
3088                DisplayPoint::new(4, 0)..DisplayPoint::new(4, 2)
3089            ]
3090        );
3091    });
3092}
3093
3094#[gpui::test]
3095fn test_move_line_up_down_with_blocks(cx: &mut TestAppContext) {
3096    init_test(cx, |_| {});
3097
3098    let editor = cx.add_window(|cx| {
3099        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
3100        build_editor(buffer, cx)
3101    });
3102    editor.update(cx, |editor, cx| {
3103        let snapshot = editor.buffer.read(cx).snapshot(cx);
3104        editor.insert_blocks(
3105            [BlockProperties {
3106                style: BlockStyle::Fixed,
3107                position: snapshot.anchor_after(Point::new(2, 0)),
3108                disposition: BlockDisposition::Below,
3109                height: 1,
3110                render: Arc::new(|_| div().into_any()),
3111            }],
3112            Some(Autoscroll::fit()),
3113            cx,
3114        );
3115        editor.change_selections(None, cx, |s| {
3116            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
3117        });
3118        editor.move_line_down(&MoveLineDown, cx);
3119    });
3120}
3121
3122//todo!(test_transpose)
3123#[gpui::test]
3124fn test_transpose(cx: &mut TestAppContext) {
3125    init_test(cx, |_| {});
3126
3127    _ = cx.add_window(|cx| {
3128        let mut editor = build_editor(MultiBuffer::build_simple("abc", cx), cx);
3129        editor.set_style(EditorStyle::default(), cx);
3130        editor.change_selections(None, cx, |s| s.select_ranges([1..1]));
3131        editor.transpose(&Default::default(), cx);
3132        assert_eq!(editor.text(cx), "bac");
3133        assert_eq!(editor.selections.ranges(cx), [2..2]);
3134
3135        editor.transpose(&Default::default(), cx);
3136        assert_eq!(editor.text(cx), "bca");
3137        assert_eq!(editor.selections.ranges(cx), [3..3]);
3138
3139        editor.transpose(&Default::default(), cx);
3140        assert_eq!(editor.text(cx), "bac");
3141        assert_eq!(editor.selections.ranges(cx), [3..3]);
3142
3143        editor
3144    });
3145
3146    _ = cx.add_window(|cx| {
3147        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx);
3148        editor.set_style(EditorStyle::default(), cx);
3149        editor.change_selections(None, cx, |s| s.select_ranges([3..3]));
3150        editor.transpose(&Default::default(), cx);
3151        assert_eq!(editor.text(cx), "acb\nde");
3152        assert_eq!(editor.selections.ranges(cx), [3..3]);
3153
3154        editor.change_selections(None, cx, |s| s.select_ranges([4..4]));
3155        editor.transpose(&Default::default(), cx);
3156        assert_eq!(editor.text(cx), "acbd\ne");
3157        assert_eq!(editor.selections.ranges(cx), [5..5]);
3158
3159        editor.transpose(&Default::default(), cx);
3160        assert_eq!(editor.text(cx), "acbde\n");
3161        assert_eq!(editor.selections.ranges(cx), [6..6]);
3162
3163        editor.transpose(&Default::default(), cx);
3164        assert_eq!(editor.text(cx), "acbd\ne");
3165        assert_eq!(editor.selections.ranges(cx), [6..6]);
3166
3167        editor
3168    });
3169
3170    _ = cx.add_window(|cx| {
3171        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx);
3172        editor.set_style(EditorStyle::default(), cx);
3173        editor.change_selections(None, cx, |s| s.select_ranges([1..1, 2..2, 4..4]));
3174        editor.transpose(&Default::default(), cx);
3175        assert_eq!(editor.text(cx), "bacd\ne");
3176        assert_eq!(editor.selections.ranges(cx), [2..2, 3..3, 5..5]);
3177
3178        editor.transpose(&Default::default(), cx);
3179        assert_eq!(editor.text(cx), "bcade\n");
3180        assert_eq!(editor.selections.ranges(cx), [3..3, 4..4, 6..6]);
3181
3182        editor.transpose(&Default::default(), cx);
3183        assert_eq!(editor.text(cx), "bcda\ne");
3184        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
3185
3186        editor.transpose(&Default::default(), cx);
3187        assert_eq!(editor.text(cx), "bcade\n");
3188        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
3189
3190        editor.transpose(&Default::default(), cx);
3191        assert_eq!(editor.text(cx), "bcaed\n");
3192        assert_eq!(editor.selections.ranges(cx), [5..5, 6..6]);
3193
3194        editor
3195    });
3196
3197    _ = cx.add_window(|cx| {
3198        let mut editor = build_editor(MultiBuffer::build_simple("🍐🏀✋", cx), cx);
3199        editor.set_style(EditorStyle::default(), cx);
3200        editor.change_selections(None, cx, |s| s.select_ranges([4..4]));
3201        editor.transpose(&Default::default(), cx);
3202        assert_eq!(editor.text(cx), "🏀🍐✋");
3203        assert_eq!(editor.selections.ranges(cx), [8..8]);
3204
3205        editor.transpose(&Default::default(), cx);
3206        assert_eq!(editor.text(cx), "🏀✋🍐");
3207        assert_eq!(editor.selections.ranges(cx), [11..11]);
3208
3209        editor.transpose(&Default::default(), cx);
3210        assert_eq!(editor.text(cx), "🏀🍐✋");
3211        assert_eq!(editor.selections.ranges(cx), [11..11]);
3212
3213        editor
3214    });
3215}
3216
3217#[gpui::test]
3218async fn test_clipboard(cx: &mut gpui::TestAppContext) {
3219    init_test(cx, |_| {});
3220
3221    let mut cx = EditorTestContext::new(cx).await;
3222
3223    cx.set_state("«one✅ ˇ»two «three ˇ»four «five ˇ»six ");
3224    cx.update_editor(|e, cx| e.cut(&Cut, cx));
3225    cx.assert_editor_state("ˇtwo ˇfour ˇsix ");
3226
3227    // Paste with three cursors. Each cursor pastes one slice of the clipboard text.
3228    cx.set_state("two ˇfour ˇsix ˇ");
3229    cx.update_editor(|e, cx| e.paste(&Paste, cx));
3230    cx.assert_editor_state("two one✅ ˇfour three ˇsix five ˇ");
3231
3232    // Paste again but with only two cursors. Since the number of cursors doesn't
3233    // match the number of slices in the clipboard, the entire clipboard text
3234    // is pasted at each cursor.
3235    cx.set_state("ˇtwo one✅ four three six five ˇ");
3236    cx.update_editor(|e, cx| {
3237        e.handle_input("( ", cx);
3238        e.paste(&Paste, cx);
3239        e.handle_input(") ", cx);
3240    });
3241    cx.assert_editor_state(
3242        &([
3243            "( one✅ ",
3244            "three ",
3245            "five ) ˇtwo one✅ four three six five ( one✅ ",
3246            "three ",
3247            "five ) ˇ",
3248        ]
3249        .join("\n")),
3250    );
3251
3252    // Cut with three selections, one of which is full-line.
3253    cx.set_state(indoc! {"
3254        1«2ˇ»3
3255        4ˇ567
3256        «8ˇ»9"});
3257    cx.update_editor(|e, cx| e.cut(&Cut, cx));
3258    cx.assert_editor_state(indoc! {"
3259        1ˇ3
3260        ˇ9"});
3261
3262    // Paste with three selections, noticing how the copied selection that was full-line
3263    // gets inserted before the second cursor.
3264    cx.set_state(indoc! {"
3265        1ˇ3
32663267        «oˇ»ne"});
3268    cx.update_editor(|e, cx| e.paste(&Paste, cx));
3269    cx.assert_editor_state(indoc! {"
3270        12ˇ3
3271        4567
32723273        8ˇne"});
3274
3275    // Copy with a single cursor only, which writes the whole line into the clipboard.
3276    cx.set_state(indoc! {"
3277        The quick brown
3278        fox juˇmps over
3279        the lazy dog"});
3280    cx.update_editor(|e, cx| e.copy(&Copy, cx));
3281    assert_eq!(
3282        cx.read_from_clipboard().map(|item| item.text().to_owned()),
3283        Some("fox jumps over\n".to_owned())
3284    );
3285
3286    // Paste with three selections, noticing how the copied full-line selection is inserted
3287    // before the empty selections but replaces the selection that is non-empty.
3288    cx.set_state(indoc! {"
3289        Tˇhe quick brown
3290        «foˇ»x jumps over
3291        tˇhe lazy dog"});
3292    cx.update_editor(|e, cx| e.paste(&Paste, cx));
3293    cx.assert_editor_state(indoc! {"
3294        fox jumps over
3295        Tˇhe quick brown
3296        fox jumps over
3297        ˇx jumps over
3298        fox jumps over
3299        tˇhe lazy dog"});
3300}
3301
3302#[gpui::test]
3303async fn test_paste_multiline(cx: &mut gpui::TestAppContext) {
3304    init_test(cx, |_| {});
3305
3306    let mut cx = EditorTestContext::new(cx).await;
3307    let language = Arc::new(Language::new(
3308        LanguageConfig::default(),
3309        Some(tree_sitter_rust::language()),
3310    ));
3311    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
3312
3313    // Cut an indented block, without the leading whitespace.
3314    cx.set_state(indoc! {"
3315        const a: B = (
3316            c(),
3317            «d(
3318                e,
3319                f
3320            )ˇ»
3321        );
3322    "});
3323    cx.update_editor(|e, cx| e.cut(&Cut, cx));
3324    cx.assert_editor_state(indoc! {"
3325        const a: B = (
3326            c(),
3327            ˇ
3328        );
3329    "});
3330
3331    // Paste it at the same position.
3332    cx.update_editor(|e, cx| e.paste(&Paste, cx));
3333    cx.assert_editor_state(indoc! {"
3334        const a: B = (
3335            c(),
3336            d(
3337                e,
3338                f
33393340        );
3341    "});
3342
3343    // Paste it at a line with a lower indent level.
3344    cx.set_state(indoc! {"
3345        ˇ
3346        const a: B = (
3347            c(),
3348        );
3349    "});
3350    cx.update_editor(|e, cx| e.paste(&Paste, cx));
3351    cx.assert_editor_state(indoc! {"
3352        d(
3353            e,
3354            f
33553356        const a: B = (
3357            c(),
3358        );
3359    "});
3360
3361    // Cut an indented block, with the leading whitespace.
3362    cx.set_state(indoc! {"
3363        const a: B = (
3364            c(),
3365        «    d(
3366                e,
3367                f
3368            )
3369        ˇ»);
3370    "});
3371    cx.update_editor(|e, cx| e.cut(&Cut, cx));
3372    cx.assert_editor_state(indoc! {"
3373        const a: B = (
3374            c(),
3375        ˇ);
3376    "});
3377
3378    // Paste it at the same position.
3379    cx.update_editor(|e, cx| e.paste(&Paste, cx));
3380    cx.assert_editor_state(indoc! {"
3381        const a: B = (
3382            c(),
3383            d(
3384                e,
3385                f
3386            )
3387        ˇ);
3388    "});
3389
3390    // Paste it at a line with a higher indent level.
3391    cx.set_state(indoc! {"
3392        const a: B = (
3393            c(),
3394            d(
3395                e,
33963397            )
3398        );
3399    "});
3400    cx.update_editor(|e, cx| e.paste(&Paste, cx));
3401    cx.assert_editor_state(indoc! {"
3402        const a: B = (
3403            c(),
3404            d(
3405                e,
3406                f    d(
3407                    e,
3408                    f
3409                )
3410        ˇ
3411            )
3412        );
3413    "});
3414}
3415
3416#[gpui::test]
3417fn test_select_all(cx: &mut TestAppContext) {
3418    init_test(cx, |_| {});
3419
3420    let view = cx.add_window(|cx| {
3421        let buffer = MultiBuffer::build_simple("abc\nde\nfgh", cx);
3422        build_editor(buffer, cx)
3423    });
3424    view.update(cx, |view, cx| {
3425        view.select_all(&SelectAll, cx);
3426        assert_eq!(
3427            view.selections.display_ranges(cx),
3428            &[DisplayPoint::new(0, 0)..DisplayPoint::new(2, 3)]
3429        );
3430    });
3431}
3432
3433#[gpui::test]
3434fn test_select_line(cx: &mut TestAppContext) {
3435    init_test(cx, |_| {});
3436
3437    let view = cx.add_window(|cx| {
3438        let buffer = MultiBuffer::build_simple(&sample_text(6, 5, 'a'), cx);
3439        build_editor(buffer, cx)
3440    });
3441    view.update(cx, |view, cx| {
3442        view.change_selections(None, cx, |s| {
3443            s.select_display_ranges([
3444                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
3445                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
3446                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
3447                DisplayPoint::new(4, 2)..DisplayPoint::new(4, 2),
3448            ])
3449        });
3450        view.select_line(&SelectLine, cx);
3451        assert_eq!(
3452            view.selections.display_ranges(cx),
3453            vec![
3454                DisplayPoint::new(0, 0)..DisplayPoint::new(2, 0),
3455                DisplayPoint::new(4, 0)..DisplayPoint::new(5, 0),
3456            ]
3457        );
3458    });
3459
3460    view.update(cx, |view, cx| {
3461        view.select_line(&SelectLine, cx);
3462        assert_eq!(
3463            view.selections.display_ranges(cx),
3464            vec![
3465                DisplayPoint::new(0, 0)..DisplayPoint::new(3, 0),
3466                DisplayPoint::new(4, 0)..DisplayPoint::new(5, 5),
3467            ]
3468        );
3469    });
3470
3471    view.update(cx, |view, cx| {
3472        view.select_line(&SelectLine, cx);
3473        assert_eq!(
3474            view.selections.display_ranges(cx),
3475            vec![DisplayPoint::new(0, 0)..DisplayPoint::new(5, 5)]
3476        );
3477    });
3478}
3479
3480#[gpui::test]
3481fn test_split_selection_into_lines(cx: &mut TestAppContext) {
3482    init_test(cx, |_| {});
3483
3484    let view = cx.add_window(|cx| {
3485        let buffer = MultiBuffer::build_simple(&sample_text(9, 5, 'a'), cx);
3486        build_editor(buffer, cx)
3487    });
3488    view.update(cx, |view, cx| {
3489        view.fold_ranges(
3490            vec![
3491                Point::new(0, 2)..Point::new(1, 2),
3492                Point::new(2, 3)..Point::new(4, 1),
3493                Point::new(7, 0)..Point::new(8, 4),
3494            ],
3495            true,
3496            cx,
3497        );
3498        view.change_selections(None, cx, |s| {
3499            s.select_display_ranges([
3500                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
3501                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
3502                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
3503                DisplayPoint::new(4, 4)..DisplayPoint::new(4, 4),
3504            ])
3505        });
3506        assert_eq!(view.display_text(cx), "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i");
3507    });
3508
3509    view.update(cx, |view, cx| {
3510        view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
3511        assert_eq!(
3512            view.display_text(cx),
3513            "aaaaa\nbbbbb\nccc⋯eeee\nfffff\nggggg\n⋯i"
3514        );
3515        assert_eq!(
3516            view.selections.display_ranges(cx),
3517            [
3518                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
3519                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
3520                DisplayPoint::new(2, 0)..DisplayPoint::new(2, 0),
3521                DisplayPoint::new(5, 4)..DisplayPoint::new(5, 4)
3522            ]
3523        );
3524    });
3525
3526    view.update(cx, |view, cx| {
3527        view.change_selections(None, cx, |s| {
3528            s.select_display_ranges([DisplayPoint::new(5, 0)..DisplayPoint::new(0, 1)])
3529        });
3530        view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
3531        assert_eq!(
3532            view.display_text(cx),
3533            "aaaaa\nbbbbb\nccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiii"
3534        );
3535        assert_eq!(
3536            view.selections.display_ranges(cx),
3537            [
3538                DisplayPoint::new(0, 5)..DisplayPoint::new(0, 5),
3539                DisplayPoint::new(1, 5)..DisplayPoint::new(1, 5),
3540                DisplayPoint::new(2, 5)..DisplayPoint::new(2, 5),
3541                DisplayPoint::new(3, 5)..DisplayPoint::new(3, 5),
3542                DisplayPoint::new(4, 5)..DisplayPoint::new(4, 5),
3543                DisplayPoint::new(5, 5)..DisplayPoint::new(5, 5),
3544                DisplayPoint::new(6, 5)..DisplayPoint::new(6, 5),
3545                DisplayPoint::new(7, 0)..DisplayPoint::new(7, 0)
3546            ]
3547        );
3548    });
3549}
3550
3551#[gpui::test]
3552async fn test_add_selection_above_below(cx: &mut TestAppContext) {
3553    init_test(cx, |_| {});
3554
3555    let mut cx = EditorTestContext::new(cx).await;
3556
3557    // let buffer = MultiBuffer::build_simple("abc\ndefghi\n\njk\nlmno\n", cx);
3558    cx.set_state(indoc!(
3559        r#"abc
3560           defˇghi
3561
3562           jk
3563           nlmo
3564           "#
3565    ));
3566
3567    cx.update_editor(|editor, cx| {
3568        editor.add_selection_above(&Default::default(), cx);
3569    });
3570
3571    cx.assert_editor_state(indoc!(
3572        r#"abcˇ
3573           defˇghi
3574
3575           jk
3576           nlmo
3577           "#
3578    ));
3579
3580    cx.update_editor(|editor, cx| {
3581        editor.add_selection_above(&Default::default(), cx);
3582    });
3583
3584    cx.assert_editor_state(indoc!(
3585        r#"abcˇ
3586            defˇghi
3587
3588            jk
3589            nlmo
3590            "#
3591    ));
3592
3593    cx.update_editor(|view, cx| {
3594        view.add_selection_below(&Default::default(), cx);
3595    });
3596
3597    cx.assert_editor_state(indoc!(
3598        r#"abc
3599           defˇghi
3600
3601           jk
3602           nlmo
3603           "#
3604    ));
3605
3606    cx.update_editor(|view, cx| {
3607        view.undo_selection(&Default::default(), cx);
3608    });
3609
3610    cx.assert_editor_state(indoc!(
3611        r#"abcˇ
3612           defˇghi
3613
3614           jk
3615           nlmo
3616           "#
3617    ));
3618
3619    cx.update_editor(|view, cx| {
3620        view.redo_selection(&Default::default(), cx);
3621    });
3622
3623    cx.assert_editor_state(indoc!(
3624        r#"abc
3625           defˇghi
3626
3627           jk
3628           nlmo
3629           "#
3630    ));
3631
3632    cx.update_editor(|view, cx| {
3633        view.add_selection_below(&Default::default(), cx);
3634    });
3635
3636    cx.assert_editor_state(indoc!(
3637        r#"abc
3638           defˇghi
3639
3640           jk
3641           nlmˇo
3642           "#
3643    ));
3644
3645    cx.update_editor(|view, cx| {
3646        view.add_selection_below(&Default::default(), cx);
3647    });
3648
3649    cx.assert_editor_state(indoc!(
3650        r#"abc
3651           defˇghi
3652
3653           jk
3654           nlmˇo
3655           "#
3656    ));
3657
3658    // change selections
3659    cx.set_state(indoc!(
3660        r#"abc
3661           def«ˇg»hi
3662
3663           jk
3664           nlmo
3665           "#
3666    ));
3667
3668    cx.update_editor(|view, cx| {
3669        view.add_selection_below(&Default::default(), cx);
3670    });
3671
3672    cx.assert_editor_state(indoc!(
3673        r#"abc
3674           def«ˇg»hi
3675
3676           jk
3677           nlm«ˇo»
3678           "#
3679    ));
3680
3681    cx.update_editor(|view, cx| {
3682        view.add_selection_below(&Default::default(), cx);
3683    });
3684
3685    cx.assert_editor_state(indoc!(
3686        r#"abc
3687           def«ˇg»hi
3688
3689           jk
3690           nlm«ˇo»
3691           "#
3692    ));
3693
3694    cx.update_editor(|view, cx| {
3695        view.add_selection_above(&Default::default(), cx);
3696    });
3697
3698    cx.assert_editor_state(indoc!(
3699        r#"abc
3700           def«ˇg»hi
3701
3702           jk
3703           nlmo
3704           "#
3705    ));
3706
3707    cx.update_editor(|view, cx| {
3708        view.add_selection_above(&Default::default(), cx);
3709    });
3710
3711    cx.assert_editor_state(indoc!(
3712        r#"abc
3713           def«ˇg»hi
3714
3715           jk
3716           nlmo
3717           "#
3718    ));
3719
3720    // Change selections again
3721    cx.set_state(indoc!(
3722        r#"a«bc
3723           defgˇ»hi
3724
3725           jk
3726           nlmo
3727           "#
3728    ));
3729
3730    cx.update_editor(|view, cx| {
3731        view.add_selection_below(&Default::default(), cx);
3732    });
3733
3734    cx.assert_editor_state(indoc!(
3735        r#"a«bcˇ»
3736           d«efgˇ»hi
3737
3738           j«kˇ»
3739           nlmo
3740           "#
3741    ));
3742
3743    cx.update_editor(|view, cx| {
3744        view.add_selection_below(&Default::default(), cx);
3745    });
3746    cx.assert_editor_state(indoc!(
3747        r#"a«bcˇ»
3748           d«efgˇ»hi
3749
3750           j«kˇ»
3751           n«lmoˇ»
3752           "#
3753    ));
3754    cx.update_editor(|view, cx| {
3755        view.add_selection_above(&Default::default(), cx);
3756    });
3757
3758    cx.assert_editor_state(indoc!(
3759        r#"a«bcˇ»
3760           d«efgˇ»hi
3761
3762           j«kˇ»
3763           nlmo
3764           "#
3765    ));
3766
3767    // Change selections again
3768    cx.set_state(indoc!(
3769        r#"abc
3770           d«ˇefghi
3771
3772           jk
3773           nlm»o
3774           "#
3775    ));
3776
3777    cx.update_editor(|view, cx| {
3778        view.add_selection_above(&Default::default(), cx);
3779    });
3780
3781    cx.assert_editor_state(indoc!(
3782        r#"a«ˇbc»
3783           d«ˇef»ghi
3784
3785           j«ˇk»
3786           n«ˇlm»o
3787           "#
3788    ));
3789
3790    cx.update_editor(|view, cx| {
3791        view.add_selection_below(&Default::default(), cx);
3792    });
3793
3794    cx.assert_editor_state(indoc!(
3795        r#"abc
3796           d«ˇef»ghi
3797
3798           j«ˇk»
3799           n«ˇlm»o
3800           "#
3801    ));
3802}
3803
3804#[gpui::test]
3805async fn test_select_next(cx: &mut gpui::TestAppContext) {
3806    init_test(cx, |_| {});
3807
3808    let mut cx = EditorTestContext::new(cx).await;
3809    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
3810
3811    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
3812        .unwrap();
3813    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
3814
3815    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
3816        .unwrap();
3817    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
3818
3819    cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
3820    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
3821
3822    cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
3823    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
3824
3825    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
3826        .unwrap();
3827    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
3828
3829    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
3830        .unwrap();
3831    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
3832}
3833
3834#[gpui::test]
3835async fn test_select_previous(cx: &mut gpui::TestAppContext) {
3836    init_test(cx, |_| {});
3837    {
3838        // `Select previous` without a selection (selects wordwise)
3839        let mut cx = EditorTestContext::new(cx).await;
3840        cx.set_state("abc\nˇabc abc\ndefabc\nabc");
3841
3842        cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
3843            .unwrap();
3844        cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
3845
3846        cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
3847            .unwrap();
3848        cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
3849
3850        cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
3851        cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
3852
3853        cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
3854        cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
3855
3856        cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
3857            .unwrap();
3858        cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\n«abcˇ»");
3859
3860        cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
3861            .unwrap();
3862        cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
3863    }
3864    {
3865        // `Select previous` with a selection
3866        let mut cx = EditorTestContext::new(cx).await;
3867        cx.set_state("abc\n«ˇabc» abc\ndefabc\nabc");
3868
3869        cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
3870            .unwrap();
3871        cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
3872
3873        cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
3874            .unwrap();
3875        cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
3876
3877        cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
3878        cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
3879
3880        cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
3881        cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
3882
3883        cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
3884            .unwrap();
3885        cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndef«abcˇ»\n«abcˇ»");
3886
3887        cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
3888            .unwrap();
3889        cx.assert_editor_state("«abcˇ»\n«ˇabc» «abcˇ»\ndef«abcˇ»\n«abcˇ»");
3890    }
3891}
3892
3893#[gpui::test]
3894async fn test_select_larger_smaller_syntax_node(cx: &mut gpui::TestAppContext) {
3895    init_test(cx, |_| {});
3896
3897    let language = Arc::new(Language::new(
3898        LanguageConfig::default(),
3899        Some(tree_sitter_rust::language()),
3900    ));
3901
3902    let text = r#"
3903        use mod1::mod2::{mod3, mod4};
3904
3905        fn fn_1(param1: bool, param2: &str) {
3906            let var1 = "text";
3907        }
3908    "#
3909    .unindent();
3910
3911    let buffer = cx.build_model(|cx| {
3912        Buffer::new(0, cx.entity_id().as_u64(), text).with_language(language, cx)
3913    });
3914    let buffer = cx.build_model(|cx| MultiBuffer::singleton(buffer, cx));
3915    let (view, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
3916
3917    view.condition::<crate::EditorEvent>(&cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
3918        .await;
3919
3920    view.update(cx, |view, cx| {
3921        view.change_selections(None, cx, |s| {
3922            s.select_display_ranges([
3923                DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
3924                DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
3925                DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18),
3926            ]);
3927        });
3928        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
3929    });
3930    assert_eq!(
3931        view.update(cx, |view, cx| { view.selections.display_ranges(cx) }),
3932        &[
3933            DisplayPoint::new(0, 23)..DisplayPoint::new(0, 27),
3934            DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
3935            DisplayPoint::new(3, 15)..DisplayPoint::new(3, 21),
3936        ]
3937    );
3938
3939    view.update(cx, |view, cx| {
3940        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
3941    });
3942    assert_eq!(
3943        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
3944        &[
3945            DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
3946            DisplayPoint::new(4, 1)..DisplayPoint::new(2, 0),
3947        ]
3948    );
3949
3950    view.update(cx, |view, cx| {
3951        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
3952    });
3953    assert_eq!(
3954        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
3955        &[DisplayPoint::new(5, 0)..DisplayPoint::new(0, 0)]
3956    );
3957
3958    // Trying to expand the selected syntax node one more time has no effect.
3959    view.update(cx, |view, cx| {
3960        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
3961    });
3962    assert_eq!(
3963        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
3964        &[DisplayPoint::new(5, 0)..DisplayPoint::new(0, 0)]
3965    );
3966
3967    view.update(cx, |view, cx| {
3968        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
3969    });
3970    assert_eq!(
3971        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
3972        &[
3973            DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
3974            DisplayPoint::new(4, 1)..DisplayPoint::new(2, 0),
3975        ]
3976    );
3977
3978    view.update(cx, |view, cx| {
3979        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
3980    });
3981    assert_eq!(
3982        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
3983        &[
3984            DisplayPoint::new(0, 23)..DisplayPoint::new(0, 27),
3985            DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
3986            DisplayPoint::new(3, 15)..DisplayPoint::new(3, 21),
3987        ]
3988    );
3989
3990    view.update(cx, |view, cx| {
3991        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
3992    });
3993    assert_eq!(
3994        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
3995        &[
3996            DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
3997            DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
3998            DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18),
3999        ]
4000    );
4001
4002    // Trying to shrink the selected syntax node one more time has no effect.
4003    view.update(cx, |view, cx| {
4004        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
4005    });
4006    assert_eq!(
4007        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
4008        &[
4009            DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
4010            DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
4011            DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18),
4012        ]
4013    );
4014
4015    // Ensure that we keep expanding the selection if the larger selection starts or ends within
4016    // a fold.
4017    view.update(cx, |view, cx| {
4018        view.fold_ranges(
4019            vec![
4020                Point::new(0, 21)..Point::new(0, 24),
4021                Point::new(3, 20)..Point::new(3, 22),
4022            ],
4023            true,
4024            cx,
4025        );
4026        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
4027    });
4028    assert_eq!(
4029        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
4030        &[
4031            DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
4032            DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
4033            DisplayPoint::new(3, 4)..DisplayPoint::new(3, 23),
4034        ]
4035    );
4036}
4037
4038#[gpui::test]
4039async fn test_autoindent_selections(cx: &mut gpui::TestAppContext) {
4040    init_test(cx, |_| {});
4041
4042    let language = Arc::new(
4043        Language::new(
4044            LanguageConfig {
4045                brackets: BracketPairConfig {
4046                    pairs: vec![
4047                        BracketPair {
4048                            start: "{".to_string(),
4049                            end: "}".to_string(),
4050                            close: false,
4051                            newline: true,
4052                        },
4053                        BracketPair {
4054                            start: "(".to_string(),
4055                            end: ")".to_string(),
4056                            close: false,
4057                            newline: true,
4058                        },
4059                    ],
4060                    ..Default::default()
4061                },
4062                ..Default::default()
4063            },
4064            Some(tree_sitter_rust::language()),
4065        )
4066        .with_indents_query(
4067            r#"
4068                (_ "(" ")" @end) @indent
4069                (_ "{" "}" @end) @indent
4070            "#,
4071        )
4072        .unwrap(),
4073    );
4074
4075    let text = "fn a() {}";
4076
4077    let buffer = cx.build_model(|cx| {
4078        Buffer::new(0, cx.entity_id().as_u64(), text).with_language(language, cx)
4079    });
4080    let buffer = cx.build_model(|cx| MultiBuffer::singleton(buffer, cx));
4081    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
4082    editor
4083        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
4084        .await;
4085
4086    editor.update(cx, |editor, cx| {
4087        editor.change_selections(None, cx, |s| s.select_ranges([5..5, 8..8, 9..9]));
4088        editor.newline(&Newline, cx);
4089        assert_eq!(editor.text(cx), "fn a(\n    \n) {\n    \n}\n");
4090        assert_eq!(
4091            editor.selections.ranges(cx),
4092            &[
4093                Point::new(1, 4)..Point::new(1, 4),
4094                Point::new(3, 4)..Point::new(3, 4),
4095                Point::new(5, 0)..Point::new(5, 0)
4096            ]
4097        );
4098    });
4099}
4100
4101#[gpui::test]
4102async fn test_autoclose_pairs(cx: &mut gpui::TestAppContext) {
4103    init_test(cx, |_| {});
4104
4105    let mut cx = EditorTestContext::new(cx).await;
4106
4107    let language = Arc::new(Language::new(
4108        LanguageConfig {
4109            brackets: BracketPairConfig {
4110                pairs: vec![
4111                    BracketPair {
4112                        start: "{".to_string(),
4113                        end: "}".to_string(),
4114                        close: true,
4115                        newline: true,
4116                    },
4117                    BracketPair {
4118                        start: "(".to_string(),
4119                        end: ")".to_string(),
4120                        close: true,
4121                        newline: true,
4122                    },
4123                    BracketPair {
4124                        start: "/*".to_string(),
4125                        end: " */".to_string(),
4126                        close: true,
4127                        newline: true,
4128                    },
4129                    BracketPair {
4130                        start: "[".to_string(),
4131                        end: "]".to_string(),
4132                        close: false,
4133                        newline: true,
4134                    },
4135                    BracketPair {
4136                        start: "\"".to_string(),
4137                        end: "\"".to_string(),
4138                        close: true,
4139                        newline: false,
4140                    },
4141                ],
4142                ..Default::default()
4143            },
4144            autoclose_before: "})]".to_string(),
4145            ..Default::default()
4146        },
4147        Some(tree_sitter_rust::language()),
4148    ));
4149
4150    let registry = Arc::new(LanguageRegistry::test());
4151    registry.add(language.clone());
4152    cx.update_buffer(|buffer, cx| {
4153        buffer.set_language_registry(registry);
4154        buffer.set_language(Some(language), cx);
4155    });
4156
4157    cx.set_state(
4158        &r#"
4159            🏀ˇ
4160            εˇ
4161            ❤️ˇ
4162        "#
4163        .unindent(),
4164    );
4165
4166    // autoclose multiple nested brackets at multiple cursors
4167    cx.update_editor(|view, cx| {
4168        view.handle_input("{", cx);
4169        view.handle_input("{", cx);
4170        view.handle_input("{", cx);
4171    });
4172    cx.assert_editor_state(
4173        &"
4174            🏀{{{ˇ}}}
4175            ε{{{ˇ}}}
4176            ❤️{{{ˇ}}}
4177        "
4178        .unindent(),
4179    );
4180
4181    // insert a different closing bracket
4182    cx.update_editor(|view, cx| {
4183        view.handle_input(")", cx);
4184    });
4185    cx.assert_editor_state(
4186        &"
4187            🏀{{{)ˇ}}}
4188            ε{{{)ˇ}}}
4189            ❤️{{{)ˇ}}}
4190        "
4191        .unindent(),
4192    );
4193
4194    // skip over the auto-closed brackets when typing a closing bracket
4195    cx.update_editor(|view, cx| {
4196        view.move_right(&MoveRight, cx);
4197        view.handle_input("}", cx);
4198        view.handle_input("}", cx);
4199        view.handle_input("}", cx);
4200    });
4201    cx.assert_editor_state(
4202        &"
4203            🏀{{{)}}}}ˇ
4204            ε{{{)}}}}ˇ
4205            ❤️{{{)}}}}ˇ
4206        "
4207        .unindent(),
4208    );
4209
4210    // autoclose multi-character pairs
4211    cx.set_state(
4212        &"
4213            ˇ
4214            ˇ
4215        "
4216        .unindent(),
4217    );
4218    cx.update_editor(|view, cx| {
4219        view.handle_input("/", cx);
4220        view.handle_input("*", cx);
4221    });
4222    cx.assert_editor_state(
4223        &"
4224            /*ˇ */
4225            /*ˇ */
4226        "
4227        .unindent(),
4228    );
4229
4230    // one cursor autocloses a multi-character pair, one cursor
4231    // does not autoclose.
4232    cx.set_state(
4233        &"
42344235            ˇ
4236        "
4237        .unindent(),
4238    );
4239    cx.update_editor(|view, cx| view.handle_input("*", cx));
4240    cx.assert_editor_state(
4241        &"
4242            /*ˇ */
42434244        "
4245        .unindent(),
4246    );
4247
4248    // Don't autoclose if the next character isn't whitespace and isn't
4249    // listed in the language's "autoclose_before" section.
4250    cx.set_state("ˇa b");
4251    cx.update_editor(|view, cx| view.handle_input("{", cx));
4252    cx.assert_editor_state("{ˇa b");
4253
4254    // Don't autoclose if `close` is false for the bracket pair
4255    cx.set_state("ˇ");
4256    cx.update_editor(|view, cx| view.handle_input("[", cx));
4257    cx.assert_editor_state("");
4258
4259    // Surround with brackets if text is selected
4260    cx.set_state("«aˇ» b");
4261    cx.update_editor(|view, cx| view.handle_input("{", cx));
4262    cx.assert_editor_state("{«aˇ»} b");
4263
4264    // Autclose pair where the start and end characters are the same
4265    cx.set_state("");
4266    cx.update_editor(|view, cx| view.handle_input("\"", cx));
4267    cx.assert_editor_state("a\"ˇ\"");
4268    cx.update_editor(|view, cx| view.handle_input("\"", cx));
4269    cx.assert_editor_state("a\"\"ˇ");
4270}
4271
4272#[gpui::test]
4273async fn test_autoclose_with_embedded_language(cx: &mut gpui::TestAppContext) {
4274    init_test(cx, |_| {});
4275
4276    let mut cx = EditorTestContext::new(cx).await;
4277
4278    let html_language = Arc::new(
4279        Language::new(
4280            LanguageConfig {
4281                name: "HTML".into(),
4282                brackets: BracketPairConfig {
4283                    pairs: vec![
4284                        BracketPair {
4285                            start: "<".into(),
4286                            end: ">".into(),
4287                            close: true,
4288                            ..Default::default()
4289                        },
4290                        BracketPair {
4291                            start: "{".into(),
4292                            end: "}".into(),
4293                            close: true,
4294                            ..Default::default()
4295                        },
4296                        BracketPair {
4297                            start: "(".into(),
4298                            end: ")".into(),
4299                            close: true,
4300                            ..Default::default()
4301                        },
4302                    ],
4303                    ..Default::default()
4304                },
4305                autoclose_before: "})]>".into(),
4306                ..Default::default()
4307            },
4308            Some(tree_sitter_html::language()),
4309        )
4310        .with_injection_query(
4311            r#"
4312            (script_element
4313                (raw_text) @content
4314                (#set! "language" "javascript"))
4315            "#,
4316        )
4317        .unwrap(),
4318    );
4319
4320    let javascript_language = Arc::new(Language::new(
4321        LanguageConfig {
4322            name: "JavaScript".into(),
4323            brackets: BracketPairConfig {
4324                pairs: vec![
4325                    BracketPair {
4326                        start: "/*".into(),
4327                        end: " */".into(),
4328                        close: true,
4329                        ..Default::default()
4330                    },
4331                    BracketPair {
4332                        start: "{".into(),
4333                        end: "}".into(),
4334                        close: true,
4335                        ..Default::default()
4336                    },
4337                    BracketPair {
4338                        start: "(".into(),
4339                        end: ")".into(),
4340                        close: true,
4341                        ..Default::default()
4342                    },
4343                ],
4344                ..Default::default()
4345            },
4346            autoclose_before: "})]>".into(),
4347            ..Default::default()
4348        },
4349        Some(tree_sitter_typescript::language_tsx()),
4350    ));
4351
4352    let registry = Arc::new(LanguageRegistry::test());
4353    registry.add(html_language.clone());
4354    registry.add(javascript_language.clone());
4355
4356    cx.update_buffer(|buffer, cx| {
4357        buffer.set_language_registry(registry);
4358        buffer.set_language(Some(html_language), cx);
4359    });
4360
4361    cx.set_state(
4362        &r#"
4363            <body>ˇ
4364                <script>
4365                    var x = 1;ˇ
4366                </script>
4367            </body>ˇ
4368        "#
4369        .unindent(),
4370    );
4371
4372    // Precondition: different languages are active at different locations.
4373    cx.update_editor(|editor, cx| {
4374        let snapshot = editor.snapshot(cx);
4375        let cursors = editor.selections.ranges::<usize>(cx);
4376        let languages = cursors
4377            .iter()
4378            .map(|c| snapshot.language_at(c.start).unwrap().name())
4379            .collect::<Vec<_>>();
4380        assert_eq!(
4381            languages,
4382            &["HTML".into(), "JavaScript".into(), "HTML".into()]
4383        );
4384    });
4385
4386    // Angle brackets autoclose in HTML, but not JavaScript.
4387    cx.update_editor(|editor, cx| {
4388        editor.handle_input("<", cx);
4389        editor.handle_input("a", cx);
4390    });
4391    cx.assert_editor_state(
4392        &r#"
4393            <body><aˇ>
4394                <script>
4395                    var x = 1;<aˇ
4396                </script>
4397            </body><aˇ>
4398        "#
4399        .unindent(),
4400    );
4401
4402    // Curly braces and parens autoclose in both HTML and JavaScript.
4403    cx.update_editor(|editor, cx| {
4404        editor.handle_input(" b=", cx);
4405        editor.handle_input("{", cx);
4406        editor.handle_input("c", cx);
4407        editor.handle_input("(", cx);
4408    });
4409    cx.assert_editor_state(
4410        &r#"
4411            <body><a b={c(ˇ)}>
4412                <script>
4413                    var x = 1;<a b={c(ˇ)}
4414                </script>
4415            </body><a b={c(ˇ)}>
4416        "#
4417        .unindent(),
4418    );
4419
4420    // Brackets that were already autoclosed are skipped.
4421    cx.update_editor(|editor, cx| {
4422        editor.handle_input(")", cx);
4423        editor.handle_input("d", cx);
4424        editor.handle_input("}", cx);
4425    });
4426    cx.assert_editor_state(
4427        &r#"
4428            <body><a b={c()d}ˇ>
4429                <script>
4430                    var x = 1;<a b={c()d}ˇ
4431                </script>
4432            </body><a b={c()d}ˇ>
4433        "#
4434        .unindent(),
4435    );
4436    cx.update_editor(|editor, cx| {
4437        editor.handle_input(">", cx);
4438    });
4439    cx.assert_editor_state(
4440        &r#"
4441            <body><a b={c()d}>ˇ
4442                <script>
4443                    var x = 1;<a b={c()d}>ˇ
4444                </script>
4445            </body><a b={c()d}>ˇ
4446        "#
4447        .unindent(),
4448    );
4449
4450    // Reset
4451    cx.set_state(
4452        &r#"
4453            <body>ˇ
4454                <script>
4455                    var x = 1;ˇ
4456                </script>
4457            </body>ˇ
4458        "#
4459        .unindent(),
4460    );
4461
4462    cx.update_editor(|editor, cx| {
4463        editor.handle_input("<", cx);
4464    });
4465    cx.assert_editor_state(
4466        &r#"
4467            <body><ˇ>
4468                <script>
4469                    var x = 1;<ˇ
4470                </script>
4471            </body><ˇ>
4472        "#
4473        .unindent(),
4474    );
4475
4476    // When backspacing, the closing angle brackets are removed.
4477    cx.update_editor(|editor, cx| {
4478        editor.backspace(&Backspace, cx);
4479    });
4480    cx.assert_editor_state(
4481        &r#"
4482            <body>ˇ
4483                <script>
4484                    var x = 1;ˇ
4485                </script>
4486            </body>ˇ
4487        "#
4488        .unindent(),
4489    );
4490
4491    // Block comments autoclose in JavaScript, but not HTML.
4492    cx.update_editor(|editor, cx| {
4493        editor.handle_input("/", cx);
4494        editor.handle_input("*", cx);
4495    });
4496    cx.assert_editor_state(
4497        &r#"
4498            <body>/*ˇ
4499                <script>
4500                    var x = 1;/*ˇ */
4501                </script>
4502            </body>/*ˇ
4503        "#
4504        .unindent(),
4505    );
4506}
4507
4508#[gpui::test]
4509async fn test_autoclose_with_overrides(cx: &mut gpui::TestAppContext) {
4510    init_test(cx, |_| {});
4511
4512    let mut cx = EditorTestContext::new(cx).await;
4513
4514    let rust_language = Arc::new(
4515        Language::new(
4516            LanguageConfig {
4517                name: "Rust".into(),
4518                brackets: serde_json::from_value(json!([
4519                    { "start": "{", "end": "}", "close": true, "newline": true },
4520                    { "start": "\"", "end": "\"", "close": true, "newline": false, "not_in": ["string"] },
4521                ]))
4522                .unwrap(),
4523                autoclose_before: "})]>".into(),
4524                ..Default::default()
4525            },
4526            Some(tree_sitter_rust::language()),
4527        )
4528        .with_override_query("(string_literal) @string")
4529        .unwrap(),
4530    );
4531
4532    let registry = Arc::new(LanguageRegistry::test());
4533    registry.add(rust_language.clone());
4534
4535    cx.update_buffer(|buffer, cx| {
4536        buffer.set_language_registry(registry);
4537        buffer.set_language(Some(rust_language), cx);
4538    });
4539
4540    cx.set_state(
4541        &r#"
4542            let x = ˇ
4543        "#
4544        .unindent(),
4545    );
4546
4547    // Inserting a quotation mark. A closing quotation mark is automatically inserted.
4548    cx.update_editor(|editor, cx| {
4549        editor.handle_input("\"", cx);
4550    });
4551    cx.assert_editor_state(
4552        &r#"
4553            let x = "ˇ"
4554        "#
4555        .unindent(),
4556    );
4557
4558    // Inserting another quotation mark. The cursor moves across the existing
4559    // automatically-inserted quotation mark.
4560    cx.update_editor(|editor, cx| {
4561        editor.handle_input("\"", cx);
4562    });
4563    cx.assert_editor_state(
4564        &r#"
4565            let x = ""ˇ
4566        "#
4567        .unindent(),
4568    );
4569
4570    // Reset
4571    cx.set_state(
4572        &r#"
4573            let x = ˇ
4574        "#
4575        .unindent(),
4576    );
4577
4578    // Inserting a quotation mark inside of a string. A second quotation mark is not inserted.
4579    cx.update_editor(|editor, cx| {
4580        editor.handle_input("\"", cx);
4581        editor.handle_input(" ", cx);
4582        editor.move_left(&Default::default(), cx);
4583        editor.handle_input("\\", cx);
4584        editor.handle_input("\"", cx);
4585    });
4586    cx.assert_editor_state(
4587        &r#"
4588            let x = "\"ˇ "
4589        "#
4590        .unindent(),
4591    );
4592
4593    // Inserting a closing quotation mark at the position of an automatically-inserted quotation
4594    // mark. Nothing is inserted.
4595    cx.update_editor(|editor, cx| {
4596        editor.move_right(&Default::default(), cx);
4597        editor.handle_input("\"", cx);
4598    });
4599    cx.assert_editor_state(
4600        &r#"
4601            let x = "\" "ˇ
4602        "#
4603        .unindent(),
4604    );
4605}
4606
4607#[gpui::test]
4608async fn test_surround_with_pair(cx: &mut gpui::TestAppContext) {
4609    init_test(cx, |_| {});
4610
4611    let language = Arc::new(Language::new(
4612        LanguageConfig {
4613            brackets: BracketPairConfig {
4614                pairs: vec![
4615                    BracketPair {
4616                        start: "{".to_string(),
4617                        end: "}".to_string(),
4618                        close: true,
4619                        newline: true,
4620                    },
4621                    BracketPair {
4622                        start: "/* ".to_string(),
4623                        end: "*/".to_string(),
4624                        close: true,
4625                        ..Default::default()
4626                    },
4627                ],
4628                ..Default::default()
4629            },
4630            ..Default::default()
4631        },
4632        Some(tree_sitter_rust::language()),
4633    ));
4634
4635    let text = r#"
4636        a
4637        b
4638        c
4639    "#
4640    .unindent();
4641
4642    let buffer = cx.build_model(|cx| {
4643        Buffer::new(0, cx.entity_id().as_u64(), text).with_language(language, cx)
4644    });
4645    let buffer = cx.build_model(|cx| MultiBuffer::singleton(buffer, cx));
4646    let (view, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
4647    view.condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
4648        .await;
4649
4650    view.update(cx, |view, cx| {
4651        view.change_selections(None, cx, |s| {
4652            s.select_display_ranges([
4653                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
4654                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
4655                DisplayPoint::new(2, 0)..DisplayPoint::new(2, 1),
4656            ])
4657        });
4658
4659        view.handle_input("{", cx);
4660        view.handle_input("{", cx);
4661        view.handle_input("{", cx);
4662        assert_eq!(
4663            view.text(cx),
4664            "
4665                {{{a}}}
4666                {{{b}}}
4667                {{{c}}}
4668            "
4669            .unindent()
4670        );
4671        assert_eq!(
4672            view.selections.display_ranges(cx),
4673            [
4674                DisplayPoint::new(0, 3)..DisplayPoint::new(0, 4),
4675                DisplayPoint::new(1, 3)..DisplayPoint::new(1, 4),
4676                DisplayPoint::new(2, 3)..DisplayPoint::new(2, 4)
4677            ]
4678        );
4679
4680        view.undo(&Undo, cx);
4681        view.undo(&Undo, cx);
4682        view.undo(&Undo, cx);
4683        assert_eq!(
4684            view.text(cx),
4685            "
4686                a
4687                b
4688                c
4689            "
4690            .unindent()
4691        );
4692        assert_eq!(
4693            view.selections.display_ranges(cx),
4694            [
4695                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
4696                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
4697                DisplayPoint::new(2, 0)..DisplayPoint::new(2, 1)
4698            ]
4699        );
4700
4701        // Ensure inserting the first character of a multi-byte bracket pair
4702        // doesn't surround the selections with the bracket.
4703        view.handle_input("/", cx);
4704        assert_eq!(
4705            view.text(cx),
4706            "
4707                /
4708                /
4709                /
4710            "
4711            .unindent()
4712        );
4713        assert_eq!(
4714            view.selections.display_ranges(cx),
4715            [
4716                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
4717                DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
4718                DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1)
4719            ]
4720        );
4721
4722        view.undo(&Undo, cx);
4723        assert_eq!(
4724            view.text(cx),
4725            "
4726                a
4727                b
4728                c
4729            "
4730            .unindent()
4731        );
4732        assert_eq!(
4733            view.selections.display_ranges(cx),
4734            [
4735                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
4736                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
4737                DisplayPoint::new(2, 0)..DisplayPoint::new(2, 1)
4738            ]
4739        );
4740
4741        // Ensure inserting the last character of a multi-byte bracket pair
4742        // doesn't surround the selections with the bracket.
4743        view.handle_input("*", cx);
4744        assert_eq!(
4745            view.text(cx),
4746            "
4747                *
4748                *
4749                *
4750            "
4751            .unindent()
4752        );
4753        assert_eq!(
4754            view.selections.display_ranges(cx),
4755            [
4756                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
4757                DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
4758                DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1)
4759            ]
4760        );
4761    });
4762}
4763
4764#[gpui::test]
4765async fn test_delete_autoclose_pair(cx: &mut gpui::TestAppContext) {
4766    init_test(cx, |_| {});
4767
4768    let language = Arc::new(Language::new(
4769        LanguageConfig {
4770            brackets: BracketPairConfig {
4771                pairs: vec![BracketPair {
4772                    start: "{".to_string(),
4773                    end: "}".to_string(),
4774                    close: true,
4775                    newline: true,
4776                }],
4777                ..Default::default()
4778            },
4779            autoclose_before: "}".to_string(),
4780            ..Default::default()
4781        },
4782        Some(tree_sitter_rust::language()),
4783    ));
4784
4785    let text = r#"
4786        a
4787        b
4788        c
4789    "#
4790    .unindent();
4791
4792    let buffer = cx.build_model(|cx| {
4793        Buffer::new(0, cx.entity_id().as_u64(), text).with_language(language, cx)
4794    });
4795    let buffer = cx.build_model(|cx| MultiBuffer::singleton(buffer, cx));
4796    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
4797    editor
4798        .condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
4799        .await;
4800
4801    editor.update(cx, |editor, cx| {
4802        editor.change_selections(None, cx, |s| {
4803            s.select_ranges([
4804                Point::new(0, 1)..Point::new(0, 1),
4805                Point::new(1, 1)..Point::new(1, 1),
4806                Point::new(2, 1)..Point::new(2, 1),
4807            ])
4808        });
4809
4810        editor.handle_input("{", cx);
4811        editor.handle_input("{", cx);
4812        editor.handle_input("_", cx);
4813        assert_eq!(
4814            editor.text(cx),
4815            "
4816                a{{_}}
4817                b{{_}}
4818                c{{_}}
4819            "
4820            .unindent()
4821        );
4822        assert_eq!(
4823            editor.selections.ranges::<Point>(cx),
4824            [
4825                Point::new(0, 4)..Point::new(0, 4),
4826                Point::new(1, 4)..Point::new(1, 4),
4827                Point::new(2, 4)..Point::new(2, 4)
4828            ]
4829        );
4830
4831        editor.backspace(&Default::default(), cx);
4832        editor.backspace(&Default::default(), cx);
4833        assert_eq!(
4834            editor.text(cx),
4835            "
4836                a{}
4837                b{}
4838                c{}
4839            "
4840            .unindent()
4841        );
4842        assert_eq!(
4843            editor.selections.ranges::<Point>(cx),
4844            [
4845                Point::new(0, 2)..Point::new(0, 2),
4846                Point::new(1, 2)..Point::new(1, 2),
4847                Point::new(2, 2)..Point::new(2, 2)
4848            ]
4849        );
4850
4851        editor.delete_to_previous_word_start(&Default::default(), cx);
4852        assert_eq!(
4853            editor.text(cx),
4854            "
4855                a
4856                b
4857                c
4858            "
4859            .unindent()
4860        );
4861        assert_eq!(
4862            editor.selections.ranges::<Point>(cx),
4863            [
4864                Point::new(0, 1)..Point::new(0, 1),
4865                Point::new(1, 1)..Point::new(1, 1),
4866                Point::new(2, 1)..Point::new(2, 1)
4867            ]
4868        );
4869    });
4870}
4871
4872// todo!(select_anchor_ranges)
4873#[gpui::test]
4874async fn test_snippets(cx: &mut gpui::TestAppContext) {
4875    init_test(cx, |_| {});
4876
4877    let (text, insertion_ranges) = marked_text_ranges(
4878        indoc! {"
4879            a.ˇ b
4880            a.ˇ b
4881            a.ˇ b
4882        "},
4883        false,
4884    );
4885
4886    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
4887    let (editor, mut cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
4888
4889    editor.update(cx, |editor, cx| {
4890        let snippet = Snippet::parse("f(${1:one}, ${2:two}, ${1:three})$0").unwrap();
4891
4892        editor
4893            .insert_snippet(&insertion_ranges, snippet, cx)
4894            .unwrap();
4895
4896        fn assert(editor: &mut Editor, cx: &mut ViewContext<Editor>, marked_text: &str) {
4897            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
4898            assert_eq!(editor.text(cx), expected_text);
4899            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
4900        }
4901
4902        assert(
4903            editor,
4904            cx,
4905            indoc! {"
4906                a.f(«one», two, «three») b
4907                a.f(«one», two, «three») b
4908                a.f(«one», two, «three») b
4909            "},
4910        );
4911
4912        // Can't move earlier than the first tab stop
4913        assert!(!editor.move_to_prev_snippet_tabstop(cx));
4914        assert(
4915            editor,
4916            cx,
4917            indoc! {"
4918                a.f(«one», two, «three») b
4919                a.f(«one», two, «three») b
4920                a.f(«one», two, «three») b
4921            "},
4922        );
4923
4924        assert!(editor.move_to_next_snippet_tabstop(cx));
4925        assert(
4926            editor,
4927            cx,
4928            indoc! {"
4929                a.f(one, «two», three) b
4930                a.f(one, «two», three) b
4931                a.f(one, «two», three) b
4932            "},
4933        );
4934
4935        editor.move_to_prev_snippet_tabstop(cx);
4936        assert(
4937            editor,
4938            cx,
4939            indoc! {"
4940                a.f(«one», two, «three») b
4941                a.f(«one», two, «three») b
4942                a.f(«one», two, «three») b
4943            "},
4944        );
4945
4946        assert!(editor.move_to_next_snippet_tabstop(cx));
4947        assert(
4948            editor,
4949            cx,
4950            indoc! {"
4951                a.f(one, «two», three) b
4952                a.f(one, «two», three) b
4953                a.f(one, «two», three) b
4954            "},
4955        );
4956        assert!(editor.move_to_next_snippet_tabstop(cx));
4957        assert(
4958            editor,
4959            cx,
4960            indoc! {"
4961                a.f(one, two, three)ˇ b
4962                a.f(one, two, three)ˇ b
4963                a.f(one, two, three)ˇ b
4964            "},
4965        );
4966
4967        // As soon as the last tab stop is reached, snippet state is gone
4968        editor.move_to_prev_snippet_tabstop(cx);
4969        assert(
4970            editor,
4971            cx,
4972            indoc! {"
4973                a.f(one, two, three)ˇ b
4974                a.f(one, two, three)ˇ b
4975                a.f(one, two, three)ˇ b
4976            "},
4977        );
4978    });
4979}
4980
4981#[gpui::test]
4982async fn test_document_format_during_save(cx: &mut gpui::TestAppContext) {
4983    init_test(cx, |_| {});
4984
4985    let mut language = Language::new(
4986        LanguageConfig {
4987            name: "Rust".into(),
4988            path_suffixes: vec!["rs".to_string()],
4989            ..Default::default()
4990        },
4991        Some(tree_sitter_rust::language()),
4992    );
4993    let mut fake_servers = language
4994        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
4995            capabilities: lsp::ServerCapabilities {
4996                document_formatting_provider: Some(lsp::OneOf::Left(true)),
4997                ..Default::default()
4998            },
4999            ..Default::default()
5000        }))
5001        .await;
5002
5003    let fs = FakeFs::new(cx.executor());
5004    fs.insert_file("/file.rs", Default::default()).await;
5005
5006    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
5007    project.update(cx, |project, _| project.languages().add(Arc::new(language)));
5008    let buffer = project
5009        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
5010        .await
5011        .unwrap();
5012
5013    cx.executor().start_waiting();
5014    let fake_server = fake_servers.next().await.unwrap();
5015
5016    let buffer = cx.build_model(|cx| MultiBuffer::singleton(buffer, cx));
5017    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
5018    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
5019    assert!(cx.read(|cx| editor.is_dirty(cx)));
5020
5021    let save = editor
5022        .update(cx, |editor, cx| editor.save(project.clone(), cx))
5023        .unwrap();
5024    fake_server
5025        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
5026            assert_eq!(
5027                params.text_document.uri,
5028                lsp::Url::from_file_path("/file.rs").unwrap()
5029            );
5030            assert_eq!(params.options.tab_size, 4);
5031            Ok(Some(vec![lsp::TextEdit::new(
5032                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
5033                ", ".to_string(),
5034            )]))
5035        })
5036        .next()
5037        .await;
5038    cx.executor().start_waiting();
5039    let x = save.await;
5040
5041    assert_eq!(
5042        editor.update(cx, |editor, cx| editor.text(cx)),
5043        "one, two\nthree\n"
5044    );
5045    assert!(!cx.read(|cx| editor.is_dirty(cx)));
5046
5047    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
5048    assert!(cx.read(|cx| editor.is_dirty(cx)));
5049
5050    // Ensure we can still save even if formatting hangs.
5051    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
5052        assert_eq!(
5053            params.text_document.uri,
5054            lsp::Url::from_file_path("/file.rs").unwrap()
5055        );
5056        futures::future::pending::<()>().await;
5057        unreachable!()
5058    });
5059    let save = editor
5060        .update(cx, |editor, cx| editor.save(project.clone(), cx))
5061        .unwrap();
5062    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
5063    cx.executor().start_waiting();
5064    save.await;
5065    assert_eq!(
5066        editor.update(cx, |editor, cx| editor.text(cx)),
5067        "one\ntwo\nthree\n"
5068    );
5069    assert!(!cx.read(|cx| editor.is_dirty(cx)));
5070
5071    // Set rust language override and assert overridden tabsize is sent to language server
5072    update_test_language_settings(cx, |settings| {
5073        settings.languages.insert(
5074            "Rust".into(),
5075            LanguageSettingsContent {
5076                tab_size: NonZeroU32::new(8),
5077                ..Default::default()
5078            },
5079        );
5080    });
5081
5082    let save = editor
5083        .update(cx, |editor, cx| editor.save(project.clone(), cx))
5084        .unwrap();
5085    fake_server
5086        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
5087            assert_eq!(
5088                params.text_document.uri,
5089                lsp::Url::from_file_path("/file.rs").unwrap()
5090            );
5091            assert_eq!(params.options.tab_size, 8);
5092            Ok(Some(vec![]))
5093        })
5094        .next()
5095        .await;
5096    cx.executor().start_waiting();
5097    save.await;
5098}
5099
5100#[gpui::test]
5101async fn test_range_format_during_save(cx: &mut gpui::TestAppContext) {
5102    init_test(cx, |_| {});
5103
5104    let mut language = Language::new(
5105        LanguageConfig {
5106            name: "Rust".into(),
5107            path_suffixes: vec!["rs".to_string()],
5108            ..Default::default()
5109        },
5110        Some(tree_sitter_rust::language()),
5111    );
5112    let mut fake_servers = language
5113        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
5114            capabilities: lsp::ServerCapabilities {
5115                document_range_formatting_provider: Some(lsp::OneOf::Left(true)),
5116                ..Default::default()
5117            },
5118            ..Default::default()
5119        }))
5120        .await;
5121
5122    let fs = FakeFs::new(cx.executor());
5123    fs.insert_file("/file.rs", Default::default()).await;
5124
5125    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
5126    project.update(cx, |project, _| project.languages().add(Arc::new(language)));
5127    let buffer = project
5128        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
5129        .await
5130        .unwrap();
5131
5132    cx.executor().start_waiting();
5133    let fake_server = fake_servers.next().await.unwrap();
5134
5135    let buffer = cx.build_model(|cx| MultiBuffer::singleton(buffer, cx));
5136    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
5137    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
5138    assert!(cx.read(|cx| editor.is_dirty(cx)));
5139
5140    let save = editor
5141        .update(cx, |editor, cx| editor.save(project.clone(), cx))
5142        .unwrap();
5143    fake_server
5144        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
5145            assert_eq!(
5146                params.text_document.uri,
5147                lsp::Url::from_file_path("/file.rs").unwrap()
5148            );
5149            assert_eq!(params.options.tab_size, 4);
5150            Ok(Some(vec![lsp::TextEdit::new(
5151                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
5152                ", ".to_string(),
5153            )]))
5154        })
5155        .next()
5156        .await;
5157    cx.executor().start_waiting();
5158    save.await;
5159    assert_eq!(
5160        editor.update(cx, |editor, cx| editor.text(cx)),
5161        "one, two\nthree\n"
5162    );
5163    assert!(!cx.read(|cx| editor.is_dirty(cx)));
5164
5165    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
5166    assert!(cx.read(|cx| editor.is_dirty(cx)));
5167
5168    // Ensure we can still save even if formatting hangs.
5169    fake_server.handle_request::<lsp::request::RangeFormatting, _, _>(
5170        move |params, _| async move {
5171            assert_eq!(
5172                params.text_document.uri,
5173                lsp::Url::from_file_path("/file.rs").unwrap()
5174            );
5175            futures::future::pending::<()>().await;
5176            unreachable!()
5177        },
5178    );
5179    let save = editor
5180        .update(cx, |editor, cx| editor.save(project.clone(), cx))
5181        .unwrap();
5182    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
5183    cx.executor().start_waiting();
5184    save.await;
5185    assert_eq!(
5186        editor.update(cx, |editor, cx| editor.text(cx)),
5187        "one\ntwo\nthree\n"
5188    );
5189    assert!(!cx.read(|cx| editor.is_dirty(cx)));
5190
5191    // Set rust language override and assert overridden tabsize is sent to language server
5192    update_test_language_settings(cx, |settings| {
5193        settings.languages.insert(
5194            "Rust".into(),
5195            LanguageSettingsContent {
5196                tab_size: NonZeroU32::new(8),
5197                ..Default::default()
5198            },
5199        );
5200    });
5201
5202    let save = editor
5203        .update(cx, |editor, cx| editor.save(project.clone(), cx))
5204        .unwrap();
5205    fake_server
5206        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
5207            assert_eq!(
5208                params.text_document.uri,
5209                lsp::Url::from_file_path("/file.rs").unwrap()
5210            );
5211            assert_eq!(params.options.tab_size, 8);
5212            Ok(Some(vec![]))
5213        })
5214        .next()
5215        .await;
5216    cx.executor().start_waiting();
5217    save.await;
5218}
5219
5220#[gpui::test]
5221async fn test_document_format_manual_trigger(cx: &mut gpui::TestAppContext) {
5222    init_test(cx, |settings| {
5223        settings.defaults.formatter = Some(language_settings::Formatter::LanguageServer)
5224    });
5225
5226    let mut language = Language::new(
5227        LanguageConfig {
5228            name: "Rust".into(),
5229            path_suffixes: vec!["rs".to_string()],
5230            // Enable Prettier formatting for the same buffer, and ensure
5231            // LSP is called instead of Prettier.
5232            prettier_parser_name: Some("test_parser".to_string()),
5233            ..Default::default()
5234        },
5235        Some(tree_sitter_rust::language()),
5236    );
5237    let mut fake_servers = language
5238        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
5239            capabilities: lsp::ServerCapabilities {
5240                document_formatting_provider: Some(lsp::OneOf::Left(true)),
5241                ..Default::default()
5242            },
5243            ..Default::default()
5244        }))
5245        .await;
5246
5247    let fs = FakeFs::new(cx.executor());
5248    fs.insert_file("/file.rs", Default::default()).await;
5249
5250    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
5251    project.update(cx, |project, _| {
5252        project.languages().add(Arc::new(language));
5253    });
5254    let buffer = project
5255        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
5256        .await
5257        .unwrap();
5258
5259    cx.executor().start_waiting();
5260    let fake_server = fake_servers.next().await.unwrap();
5261
5262    let buffer = cx.build_model(|cx| MultiBuffer::singleton(buffer, cx));
5263    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
5264    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
5265
5266    let format = editor
5267        .update(cx, |editor, cx| {
5268            editor.perform_format(project.clone(), FormatTrigger::Manual, cx)
5269        })
5270        .unwrap();
5271    fake_server
5272        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
5273            assert_eq!(
5274                params.text_document.uri,
5275                lsp::Url::from_file_path("/file.rs").unwrap()
5276            );
5277            assert_eq!(params.options.tab_size, 4);
5278            Ok(Some(vec![lsp::TextEdit::new(
5279                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
5280                ", ".to_string(),
5281            )]))
5282        })
5283        .next()
5284        .await;
5285    cx.executor().start_waiting();
5286    format.await;
5287    assert_eq!(
5288        editor.update(cx, |editor, cx| editor.text(cx)),
5289        "one, two\nthree\n"
5290    );
5291
5292    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
5293    // Ensure we don't lock if formatting hangs.
5294    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
5295        assert_eq!(
5296            params.text_document.uri,
5297            lsp::Url::from_file_path("/file.rs").unwrap()
5298        );
5299        futures::future::pending::<()>().await;
5300        unreachable!()
5301    });
5302    let format = editor
5303        .update(cx, |editor, cx| {
5304            editor.perform_format(project, FormatTrigger::Manual, cx)
5305        })
5306        .unwrap();
5307    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
5308    cx.executor().start_waiting();
5309    format.await;
5310    assert_eq!(
5311        editor.update(cx, |editor, cx| editor.text(cx)),
5312        "one\ntwo\nthree\n"
5313    );
5314}
5315
5316#[gpui::test]
5317async fn test_concurrent_format_requests(cx: &mut gpui::TestAppContext) {
5318    init_test(cx, |_| {});
5319
5320    let mut cx = EditorLspTestContext::new_rust(
5321        lsp::ServerCapabilities {
5322            document_formatting_provider: Some(lsp::OneOf::Left(true)),
5323            ..Default::default()
5324        },
5325        cx,
5326    )
5327    .await;
5328
5329    cx.set_state(indoc! {"
5330        one.twoˇ
5331    "});
5332
5333    // The format request takes a long time. When it completes, it inserts
5334    // a newline and an indent before the `.`
5335    cx.lsp
5336        .handle_request::<lsp::request::Formatting, _, _>(move |_, cx| {
5337            let executor = cx.background_executor().clone();
5338            async move {
5339                executor.timer(Duration::from_millis(100)).await;
5340                Ok(Some(vec![lsp::TextEdit {
5341                    range: lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 3)),
5342                    new_text: "\n    ".into(),
5343                }]))
5344            }
5345        });
5346
5347    // Submit a format request.
5348    let format_1 = cx
5349        .update_editor(|editor, cx| editor.format(&Format, cx))
5350        .unwrap();
5351    cx.executor().run_until_parked();
5352
5353    // Submit a second format request.
5354    let format_2 = cx
5355        .update_editor(|editor, cx| editor.format(&Format, cx))
5356        .unwrap();
5357    cx.executor().run_until_parked();
5358
5359    // Wait for both format requests to complete
5360    cx.executor().advance_clock(Duration::from_millis(200));
5361    cx.executor().start_waiting();
5362    format_1.await.unwrap();
5363    cx.executor().start_waiting();
5364    format_2.await.unwrap();
5365
5366    // The formatting edits only happens once.
5367    cx.assert_editor_state(indoc! {"
5368        one
5369            .twoˇ
5370    "});
5371}
5372
5373#[gpui::test]
5374async fn test_strip_whitespace_and_format_via_lsp(cx: &mut gpui::TestAppContext) {
5375    init_test(cx, |settings| {
5376        settings.defaults.formatter = Some(language_settings::Formatter::Auto)
5377    });
5378
5379    let mut cx = EditorLspTestContext::new_rust(
5380        lsp::ServerCapabilities {
5381            document_formatting_provider: Some(lsp::OneOf::Left(true)),
5382            ..Default::default()
5383        },
5384        cx,
5385    )
5386    .await;
5387
5388    // Set up a buffer white some trailing whitespace and no trailing newline.
5389    cx.set_state(
5390        &[
5391            "one ",   //
5392            "twoˇ",   //
5393            "three ", //
5394            "four",   //
5395        ]
5396        .join("\n"),
5397    );
5398
5399    // Submit a format request.
5400    let format = cx
5401        .update_editor(|editor, cx| editor.format(&Format, cx))
5402        .unwrap();
5403
5404    // Record which buffer changes have been sent to the language server
5405    let buffer_changes = Arc::new(Mutex::new(Vec::new()));
5406    cx.lsp
5407        .handle_notification::<lsp::notification::DidChangeTextDocument, _>({
5408            let buffer_changes = buffer_changes.clone();
5409            move |params, _| {
5410                buffer_changes.lock().extend(
5411                    params
5412                        .content_changes
5413                        .into_iter()
5414                        .map(|e| (e.range.unwrap(), e.text)),
5415                );
5416            }
5417        });
5418
5419    // Handle formatting requests to the language server.
5420    cx.lsp.handle_request::<lsp::request::Formatting, _, _>({
5421        let buffer_changes = buffer_changes.clone();
5422        move |_, _| {
5423            // When formatting is requested, trailing whitespace has already been stripped,
5424            // and the trailing newline has already been added.
5425            assert_eq!(
5426                &buffer_changes.lock()[1..],
5427                &[
5428                    (
5429                        lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 4)),
5430                        "".into()
5431                    ),
5432                    (
5433                        lsp::Range::new(lsp::Position::new(2, 5), lsp::Position::new(2, 6)),
5434                        "".into()
5435                    ),
5436                    (
5437                        lsp::Range::new(lsp::Position::new(3, 4), lsp::Position::new(3, 4)),
5438                        "\n".into()
5439                    ),
5440                ]
5441            );
5442
5443            // Insert blank lines between each line of the buffer.
5444            async move {
5445                Ok(Some(vec![
5446                    lsp::TextEdit {
5447                        range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 0)),
5448                        new_text: "\n".into(),
5449                    },
5450                    lsp::TextEdit {
5451                        range: lsp::Range::new(lsp::Position::new(2, 0), lsp::Position::new(2, 0)),
5452                        new_text: "\n".into(),
5453                    },
5454                ]))
5455            }
5456        }
5457    });
5458
5459    // After formatting the buffer, the trailing whitespace is stripped,
5460    // a newline is appended, and the edits provided by the language server
5461    // have been applied.
5462    format.await.unwrap();
5463    cx.assert_editor_state(
5464        &[
5465            "one",   //
5466            "",      //
5467            "twoˇ",  //
5468            "",      //
5469            "three", //
5470            "four",  //
5471            "",      //
5472        ]
5473        .join("\n"),
5474    );
5475
5476    // Undoing the formatting undoes the trailing whitespace removal, the
5477    // trailing newline, and the LSP edits.
5478    cx.update_buffer(|buffer, cx| buffer.undo(cx));
5479    cx.assert_editor_state(
5480        &[
5481            "one ",   //
5482            "twoˇ",   //
5483            "three ", //
5484            "four",   //
5485        ]
5486        .join("\n"),
5487    );
5488}
5489
5490#[gpui::test]
5491async fn test_completion(cx: &mut gpui::TestAppContext) {
5492    init_test(cx, |_| {});
5493
5494    let mut cx = EditorLspTestContext::new_rust(
5495        lsp::ServerCapabilities {
5496            completion_provider: Some(lsp::CompletionOptions {
5497                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
5498                resolve_provider: Some(true),
5499                ..Default::default()
5500            }),
5501            ..Default::default()
5502        },
5503        cx,
5504    )
5505    .await;
5506
5507    cx.set_state(indoc! {"
5508        oneˇ
5509        two
5510        three
5511    "});
5512    cx.simulate_keystroke(".");
5513    handle_completion_request(
5514        &mut cx,
5515        indoc! {"
5516            one.|<>
5517            two
5518            three
5519        "},
5520        vec!["first_completion", "second_completion"],
5521    )
5522    .await;
5523    cx.condition(|editor, _| editor.context_menu_visible())
5524        .await;
5525    let apply_additional_edits = cx.update_editor(|editor, cx| {
5526        editor.context_menu_next(&Default::default(), cx);
5527        editor
5528            .confirm_completion(&ConfirmCompletion::default(), cx)
5529            .unwrap()
5530    });
5531    cx.assert_editor_state(indoc! {"
5532        one.second_completionˇ
5533        two
5534        three
5535    "});
5536
5537    handle_resolve_completion_request(
5538        &mut cx,
5539        Some(vec![
5540            (
5541                //This overlaps with the primary completion edit which is
5542                //misbehavior from the LSP spec, test that we filter it out
5543                indoc! {"
5544                    one.second_ˇcompletion
5545                    two
5546                    threeˇ
5547                "},
5548                "overlapping additional edit",
5549            ),
5550            (
5551                indoc! {"
5552                    one.second_completion
5553                    two
5554                    threeˇ
5555                "},
5556                "\nadditional edit",
5557            ),
5558        ]),
5559    )
5560    .await;
5561    apply_additional_edits.await.unwrap();
5562    cx.assert_editor_state(indoc! {"
5563        one.second_completionˇ
5564        two
5565        three
5566        additional edit
5567    "});
5568
5569    cx.set_state(indoc! {"
5570        one.second_completion
5571        twoˇ
5572        threeˇ
5573        additional edit
5574    "});
5575    cx.simulate_keystroke(" ");
5576    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
5577    cx.simulate_keystroke("s");
5578    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
5579
5580    cx.assert_editor_state(indoc! {"
5581        one.second_completion
5582        two sˇ
5583        three sˇ
5584        additional edit
5585    "});
5586    handle_completion_request(
5587        &mut cx,
5588        indoc! {"
5589            one.second_completion
5590            two s
5591            three <s|>
5592            additional edit
5593        "},
5594        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
5595    )
5596    .await;
5597    cx.condition(|editor, _| editor.context_menu_visible())
5598        .await;
5599
5600    cx.simulate_keystroke("i");
5601
5602    handle_completion_request(
5603        &mut cx,
5604        indoc! {"
5605            one.second_completion
5606            two si
5607            three <si|>
5608            additional edit
5609        "},
5610        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
5611    )
5612    .await;
5613    cx.condition(|editor, _| editor.context_menu_visible())
5614        .await;
5615
5616    let apply_additional_edits = cx.update_editor(|editor, cx| {
5617        editor
5618            .confirm_completion(&ConfirmCompletion::default(), cx)
5619            .unwrap()
5620    });
5621    cx.assert_editor_state(indoc! {"
5622        one.second_completion
5623        two sixth_completionˇ
5624        three sixth_completionˇ
5625        additional edit
5626    "});
5627
5628    handle_resolve_completion_request(&mut cx, None).await;
5629    apply_additional_edits.await.unwrap();
5630
5631    cx.update(|cx| {
5632        cx.update_global::<SettingsStore, _>(|settings, cx| {
5633            settings.update_user_settings::<EditorSettings>(cx, |settings| {
5634                settings.show_completions_on_input = Some(false);
5635            });
5636        })
5637    });
5638    cx.set_state("editorˇ");
5639    cx.simulate_keystroke(".");
5640    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
5641    cx.simulate_keystroke("c");
5642    cx.simulate_keystroke("l");
5643    cx.simulate_keystroke("o");
5644    cx.assert_editor_state("editor.cloˇ");
5645    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
5646    cx.update_editor(|editor, cx| {
5647        editor.show_completions(&ShowCompletions, cx);
5648    });
5649    handle_completion_request(&mut cx, "editor.<clo|>", vec!["close", "clobber"]).await;
5650    cx.condition(|editor, _| editor.context_menu_visible())
5651        .await;
5652    let apply_additional_edits = cx.update_editor(|editor, cx| {
5653        editor
5654            .confirm_completion(&ConfirmCompletion::default(), cx)
5655            .unwrap()
5656    });
5657    cx.assert_editor_state("editor.closeˇ");
5658    handle_resolve_completion_request(&mut cx, None).await;
5659    apply_additional_edits.await.unwrap();
5660}
5661
5662#[gpui::test]
5663async fn test_toggle_comment(cx: &mut gpui::TestAppContext) {
5664    init_test(cx, |_| {});
5665    let mut cx = EditorTestContext::new(cx).await;
5666    let language = Arc::new(Language::new(
5667        LanguageConfig {
5668            line_comment: Some("// ".into()),
5669            ..Default::default()
5670        },
5671        Some(tree_sitter_rust::language()),
5672    ));
5673    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
5674
5675    // If multiple selections intersect a line, the line is only toggled once.
5676    cx.set_state(indoc! {"
5677        fn a() {
5678            «//b();
5679            ˇ»// «c();
5680            //ˇ»  d();
5681        }
5682    "});
5683
5684    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
5685
5686    cx.assert_editor_state(indoc! {"
5687        fn a() {
5688            «b();
5689            c();
5690            ˇ» d();
5691        }
5692    "});
5693
5694    // The comment prefix is inserted at the same column for every line in a
5695    // selection.
5696    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
5697
5698    cx.assert_editor_state(indoc! {"
5699        fn a() {
5700            // «b();
5701            // c();
5702            ˇ»//  d();
5703        }
5704    "});
5705
5706    // If a selection ends at the beginning of a line, that line is not toggled.
5707    cx.set_selections_state(indoc! {"
5708        fn a() {
5709            // b();
5710            «// c();
5711        ˇ»    //  d();
5712        }
5713    "});
5714
5715    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
5716
5717    cx.assert_editor_state(indoc! {"
5718        fn a() {
5719            // b();
5720            «c();
5721        ˇ»    //  d();
5722        }
5723    "});
5724
5725    // If a selection span a single line and is empty, the line is toggled.
5726    cx.set_state(indoc! {"
5727        fn a() {
5728            a();
5729            b();
5730        ˇ
5731        }
5732    "});
5733
5734    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
5735
5736    cx.assert_editor_state(indoc! {"
5737        fn a() {
5738            a();
5739            b();
5740        //•ˇ
5741        }
5742    "});
5743
5744    // If a selection span multiple lines, empty lines are not toggled.
5745    cx.set_state(indoc! {"
5746        fn a() {
5747            «a();
5748
5749            c();ˇ»
5750        }
5751    "});
5752
5753    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
5754
5755    cx.assert_editor_state(indoc! {"
5756        fn a() {
5757            // «a();
5758
5759            // c();ˇ»
5760        }
5761    "});
5762}
5763
5764#[gpui::test]
5765async fn test_advance_downward_on_toggle_comment(cx: &mut gpui::TestAppContext) {
5766    init_test(cx, |_| {});
5767
5768    let language = Arc::new(Language::new(
5769        LanguageConfig {
5770            line_comment: Some("// ".into()),
5771            ..Default::default()
5772        },
5773        Some(tree_sitter_rust::language()),
5774    ));
5775
5776    let registry = Arc::new(LanguageRegistry::test());
5777    registry.add(language.clone());
5778
5779    let mut cx = EditorTestContext::new(cx).await;
5780    cx.update_buffer(|buffer, cx| {
5781        buffer.set_language_registry(registry);
5782        buffer.set_language(Some(language), cx);
5783    });
5784
5785    let toggle_comments = &ToggleComments {
5786        advance_downwards: true,
5787    };
5788
5789    // Single cursor on one line -> advance
5790    // Cursor moves horizontally 3 characters as well on non-blank line
5791    cx.set_state(indoc!(
5792        "fn a() {
5793             ˇdog();
5794             cat();
5795        }"
5796    ));
5797    cx.update_editor(|editor, cx| {
5798        editor.toggle_comments(toggle_comments, cx);
5799    });
5800    cx.assert_editor_state(indoc!(
5801        "fn a() {
5802             // dog();
5803             catˇ();
5804        }"
5805    ));
5806
5807    // Single selection on one line -> don't advance
5808    cx.set_state(indoc!(
5809        "fn a() {
5810             «dog()ˇ»;
5811             cat();
5812        }"
5813    ));
5814    cx.update_editor(|editor, cx| {
5815        editor.toggle_comments(toggle_comments, cx);
5816    });
5817    cx.assert_editor_state(indoc!(
5818        "fn a() {
5819             // «dog()ˇ»;
5820             cat();
5821        }"
5822    ));
5823
5824    // Multiple cursors on one line -> advance
5825    cx.set_state(indoc!(
5826        "fn a() {
5827             ˇdˇog();
5828             cat();
5829        }"
5830    ));
5831    cx.update_editor(|editor, cx| {
5832        editor.toggle_comments(toggle_comments, cx);
5833    });
5834    cx.assert_editor_state(indoc!(
5835        "fn a() {
5836             // dog();
5837             catˇ(ˇ);
5838        }"
5839    ));
5840
5841    // Multiple cursors on one line, with selection -> don't advance
5842    cx.set_state(indoc!(
5843        "fn a() {
5844             ˇdˇog«()ˇ»;
5845             cat();
5846        }"
5847    ));
5848    cx.update_editor(|editor, cx| {
5849        editor.toggle_comments(toggle_comments, cx);
5850    });
5851    cx.assert_editor_state(indoc!(
5852        "fn a() {
5853             // ˇdˇog«()ˇ»;
5854             cat();
5855        }"
5856    ));
5857
5858    // Single cursor on one line -> advance
5859    // Cursor moves to column 0 on blank line
5860    cx.set_state(indoc!(
5861        "fn a() {
5862             ˇdog();
5863
5864             cat();
5865        }"
5866    ));
5867    cx.update_editor(|editor, cx| {
5868        editor.toggle_comments(toggle_comments, cx);
5869    });
5870    cx.assert_editor_state(indoc!(
5871        "fn a() {
5872             // dog();
5873        ˇ
5874             cat();
5875        }"
5876    ));
5877
5878    // Single cursor on one line -> advance
5879    // Cursor starts and ends at column 0
5880    cx.set_state(indoc!(
5881        "fn a() {
5882         ˇ    dog();
5883             cat();
5884        }"
5885    ));
5886    cx.update_editor(|editor, cx| {
5887        editor.toggle_comments(toggle_comments, cx);
5888    });
5889    cx.assert_editor_state(indoc!(
5890        "fn a() {
5891             // dog();
5892         ˇ    cat();
5893        }"
5894    ));
5895}
5896
5897#[gpui::test]
5898async fn test_toggle_block_comment(cx: &mut gpui::TestAppContext) {
5899    init_test(cx, |_| {});
5900
5901    let mut cx = EditorTestContext::new(cx).await;
5902
5903    let html_language = Arc::new(
5904        Language::new(
5905            LanguageConfig {
5906                name: "HTML".into(),
5907                block_comment: Some(("<!-- ".into(), " -->".into())),
5908                ..Default::default()
5909            },
5910            Some(tree_sitter_html::language()),
5911        )
5912        .with_injection_query(
5913            r#"
5914            (script_element
5915                (raw_text) @content
5916                (#set! "language" "javascript"))
5917            "#,
5918        )
5919        .unwrap(),
5920    );
5921
5922    let javascript_language = Arc::new(Language::new(
5923        LanguageConfig {
5924            name: "JavaScript".into(),
5925            line_comment: Some("// ".into()),
5926            ..Default::default()
5927        },
5928        Some(tree_sitter_typescript::language_tsx()),
5929    ));
5930
5931    let registry = Arc::new(LanguageRegistry::test());
5932    registry.add(html_language.clone());
5933    registry.add(javascript_language.clone());
5934
5935    cx.update_buffer(|buffer, cx| {
5936        buffer.set_language_registry(registry);
5937        buffer.set_language(Some(html_language), cx);
5938    });
5939
5940    // Toggle comments for empty selections
5941    cx.set_state(
5942        &r#"
5943            <p>A</p>ˇ
5944            <p>B</p>ˇ
5945            <p>C</p>ˇ
5946        "#
5947        .unindent(),
5948    );
5949    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
5950    cx.assert_editor_state(
5951        &r#"
5952            <!-- <p>A</p>ˇ -->
5953            <!-- <p>B</p>ˇ -->
5954            <!-- <p>C</p>ˇ -->
5955        "#
5956        .unindent(),
5957    );
5958    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
5959    cx.assert_editor_state(
5960        &r#"
5961            <p>A</p>ˇ
5962            <p>B</p>ˇ
5963            <p>C</p>ˇ
5964        "#
5965        .unindent(),
5966    );
5967
5968    // Toggle comments for mixture of empty and non-empty selections, where
5969    // multiple selections occupy a given line.
5970    cx.set_state(
5971        &r#"
5972            <p>A«</p>
5973            <p>ˇ»B</p>ˇ
5974            <p>C«</p>
5975            <p>ˇ»D</p>ˇ
5976        "#
5977        .unindent(),
5978    );
5979
5980    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
5981    cx.assert_editor_state(
5982        &r#"
5983            <!-- <p>A«</p>
5984            <p>ˇ»B</p>ˇ -->
5985            <!-- <p>C«</p>
5986            <p>ˇ»D</p>ˇ -->
5987        "#
5988        .unindent(),
5989    );
5990    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
5991    cx.assert_editor_state(
5992        &r#"
5993            <p>A«</p>
5994            <p>ˇ»B</p>ˇ
5995            <p>C«</p>
5996            <p>ˇ»D</p>ˇ
5997        "#
5998        .unindent(),
5999    );
6000
6001    // Toggle comments when different languages are active for different
6002    // selections.
6003    cx.set_state(
6004        &r#"
6005            ˇ<script>
6006                ˇvar x = new Y();
6007            ˇ</script>
6008        "#
6009        .unindent(),
6010    );
6011    cx.executor().run_until_parked();
6012    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
6013    cx.assert_editor_state(
6014        &r#"
6015            <!-- ˇ<script> -->
6016                // ˇvar x = new Y();
6017            <!-- ˇ</script> -->
6018        "#
6019        .unindent(),
6020    );
6021}
6022
6023#[gpui::test]
6024fn test_editing_disjoint_excerpts(cx: &mut TestAppContext) {
6025    init_test(cx, |_| {});
6026
6027    let buffer =
6028        cx.build_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), sample_text(3, 4, 'a')));
6029    let multibuffer = cx.build_model(|cx| {
6030        let mut multibuffer = MultiBuffer::new(0);
6031        multibuffer.push_excerpts(
6032            buffer.clone(),
6033            [
6034                ExcerptRange {
6035                    context: Point::new(0, 0)..Point::new(0, 4),
6036                    primary: None,
6037                },
6038                ExcerptRange {
6039                    context: Point::new(1, 0)..Point::new(1, 4),
6040                    primary: None,
6041                },
6042            ],
6043            cx,
6044        );
6045        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb");
6046        multibuffer
6047    });
6048
6049    let (view, cx) = cx.add_window_view(|cx| build_editor(multibuffer, cx));
6050    view.update(cx, |view, cx| {
6051        assert_eq!(view.text(cx), "aaaa\nbbbb");
6052        view.change_selections(None, cx, |s| {
6053            s.select_ranges([
6054                Point::new(0, 0)..Point::new(0, 0),
6055                Point::new(1, 0)..Point::new(1, 0),
6056            ])
6057        });
6058
6059        view.handle_input("X", cx);
6060        assert_eq!(view.text(cx), "Xaaaa\nXbbbb");
6061        assert_eq!(
6062            view.selections.ranges(cx),
6063            [
6064                Point::new(0, 1)..Point::new(0, 1),
6065                Point::new(1, 1)..Point::new(1, 1),
6066            ]
6067        );
6068
6069        // Ensure the cursor's head is respected when deleting across an excerpt boundary.
6070        view.change_selections(None, cx, |s| {
6071            s.select_ranges([Point::new(0, 2)..Point::new(1, 2)])
6072        });
6073        view.backspace(&Default::default(), cx);
6074        assert_eq!(view.text(cx), "Xa\nbbb");
6075        assert_eq!(
6076            view.selections.ranges(cx),
6077            [Point::new(1, 0)..Point::new(1, 0)]
6078        );
6079
6080        view.change_selections(None, cx, |s| {
6081            s.select_ranges([Point::new(1, 1)..Point::new(0, 1)])
6082        });
6083        view.backspace(&Default::default(), cx);
6084        assert_eq!(view.text(cx), "X\nbb");
6085        assert_eq!(
6086            view.selections.ranges(cx),
6087            [Point::new(0, 1)..Point::new(0, 1)]
6088        );
6089    });
6090}
6091
6092#[gpui::test]
6093fn test_editing_overlapping_excerpts(cx: &mut TestAppContext) {
6094    init_test(cx, |_| {});
6095
6096    let markers = vec![('[', ']').into(), ('(', ')').into()];
6097    let (initial_text, mut excerpt_ranges) = marked_text_ranges_by(
6098        indoc! {"
6099            [aaaa
6100            (bbbb]
6101            cccc)",
6102        },
6103        markers.clone(),
6104    );
6105    let excerpt_ranges = markers.into_iter().map(|marker| {
6106        let context = excerpt_ranges.remove(&marker).unwrap()[0].clone();
6107        ExcerptRange {
6108            context,
6109            primary: None,
6110        }
6111    });
6112    let buffer = cx.build_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), initial_text));
6113    let multibuffer = cx.build_model(|cx| {
6114        let mut multibuffer = MultiBuffer::new(0);
6115        multibuffer.push_excerpts(buffer, excerpt_ranges, cx);
6116        multibuffer
6117    });
6118
6119    let (view, cx) = cx.add_window_view(|cx| build_editor(multibuffer, cx));
6120    view.update(cx, |view, cx| {
6121        let (expected_text, selection_ranges) = marked_text_ranges(
6122            indoc! {"
6123                aaaa
6124                bˇbbb
6125                bˇbbˇb
6126                cccc"
6127            },
6128            true,
6129        );
6130        assert_eq!(view.text(cx), expected_text);
6131        view.change_selections(None, cx, |s| s.select_ranges(selection_ranges));
6132
6133        view.handle_input("X", cx);
6134
6135        let (expected_text, expected_selections) = marked_text_ranges(
6136            indoc! {"
6137                aaaa
6138                bXˇbbXb
6139                bXˇbbXˇb
6140                cccc"
6141            },
6142            false,
6143        );
6144        assert_eq!(view.text(cx), expected_text);
6145        assert_eq!(view.selections.ranges(cx), expected_selections);
6146
6147        view.newline(&Newline, cx);
6148        let (expected_text, expected_selections) = marked_text_ranges(
6149            indoc! {"
6150                aaaa
6151                bX
6152                ˇbbX
6153                b
6154                bX
6155                ˇbbX
6156                ˇb
6157                cccc"
6158            },
6159            false,
6160        );
6161        assert_eq!(view.text(cx), expected_text);
6162        assert_eq!(view.selections.ranges(cx), expected_selections);
6163    });
6164}
6165
6166#[gpui::test]
6167fn test_refresh_selections(cx: &mut TestAppContext) {
6168    init_test(cx, |_| {});
6169
6170    let buffer =
6171        cx.build_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), sample_text(3, 4, 'a')));
6172    let mut excerpt1_id = None;
6173    let multibuffer = cx.build_model(|cx| {
6174        let mut multibuffer = MultiBuffer::new(0);
6175        excerpt1_id = multibuffer
6176            .push_excerpts(
6177                buffer.clone(),
6178                [
6179                    ExcerptRange {
6180                        context: Point::new(0, 0)..Point::new(1, 4),
6181                        primary: None,
6182                    },
6183                    ExcerptRange {
6184                        context: Point::new(1, 0)..Point::new(2, 4),
6185                        primary: None,
6186                    },
6187                ],
6188                cx,
6189            )
6190            .into_iter()
6191            .next();
6192        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
6193        multibuffer
6194    });
6195
6196    let editor = cx.add_window(|cx| {
6197        let mut editor = build_editor(multibuffer.clone(), cx);
6198        let snapshot = editor.snapshot(cx);
6199        editor.change_selections(None, cx, |s| {
6200            s.select_ranges([Point::new(1, 3)..Point::new(1, 3)])
6201        });
6202        editor.begin_selection(Point::new(2, 1).to_display_point(&snapshot), true, 1, cx);
6203        assert_eq!(
6204            editor.selections.ranges(cx),
6205            [
6206                Point::new(1, 3)..Point::new(1, 3),
6207                Point::new(2, 1)..Point::new(2, 1),
6208            ]
6209        );
6210        editor
6211    });
6212
6213    // Refreshing selections is a no-op when excerpts haven't changed.
6214    editor.update(cx, |editor, cx| {
6215        editor.change_selections(None, cx, |s| s.refresh());
6216        assert_eq!(
6217            editor.selections.ranges(cx),
6218            [
6219                Point::new(1, 3)..Point::new(1, 3),
6220                Point::new(2, 1)..Point::new(2, 1),
6221            ]
6222        );
6223    });
6224
6225    multibuffer.update(cx, |multibuffer, cx| {
6226        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
6227    });
6228    editor.update(cx, |editor, cx| {
6229        // Removing an excerpt causes the first selection to become degenerate.
6230        assert_eq!(
6231            editor.selections.ranges(cx),
6232            [
6233                Point::new(0, 0)..Point::new(0, 0),
6234                Point::new(0, 1)..Point::new(0, 1)
6235            ]
6236        );
6237
6238        // Refreshing selections will relocate the first selection to the original buffer
6239        // location.
6240        editor.change_selections(None, cx, |s| s.refresh());
6241        assert_eq!(
6242            editor.selections.ranges(cx),
6243            [
6244                Point::new(0, 1)..Point::new(0, 1),
6245                Point::new(0, 3)..Point::new(0, 3)
6246            ]
6247        );
6248        assert!(editor.selections.pending_anchor().is_some());
6249    });
6250}
6251
6252#[gpui::test]
6253fn test_refresh_selections_while_selecting_with_mouse(cx: &mut TestAppContext) {
6254    init_test(cx, |_| {});
6255
6256    let buffer =
6257        cx.build_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), sample_text(3, 4, 'a')));
6258    let mut excerpt1_id = None;
6259    let multibuffer = cx.build_model(|cx| {
6260        let mut multibuffer = MultiBuffer::new(0);
6261        excerpt1_id = multibuffer
6262            .push_excerpts(
6263                buffer.clone(),
6264                [
6265                    ExcerptRange {
6266                        context: Point::new(0, 0)..Point::new(1, 4),
6267                        primary: None,
6268                    },
6269                    ExcerptRange {
6270                        context: Point::new(1, 0)..Point::new(2, 4),
6271                        primary: None,
6272                    },
6273                ],
6274                cx,
6275            )
6276            .into_iter()
6277            .next();
6278        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
6279        multibuffer
6280    });
6281
6282    let editor = cx.add_window(|cx| {
6283        let mut editor = build_editor(multibuffer.clone(), cx);
6284        let snapshot = editor.snapshot(cx);
6285        editor.begin_selection(Point::new(1, 3).to_display_point(&snapshot), false, 1, cx);
6286        assert_eq!(
6287            editor.selections.ranges(cx),
6288            [Point::new(1, 3)..Point::new(1, 3)]
6289        );
6290        editor
6291    });
6292
6293    multibuffer.update(cx, |multibuffer, cx| {
6294        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
6295    });
6296    editor.update(cx, |editor, cx| {
6297        assert_eq!(
6298            editor.selections.ranges(cx),
6299            [Point::new(0, 0)..Point::new(0, 0)]
6300        );
6301
6302        // Ensure we don't panic when selections are refreshed and that the pending selection is finalized.
6303        editor.change_selections(None, cx, |s| s.refresh());
6304        assert_eq!(
6305            editor.selections.ranges(cx),
6306            [Point::new(0, 3)..Point::new(0, 3)]
6307        );
6308        assert!(editor.selections.pending_anchor().is_some());
6309    });
6310}
6311
6312#[gpui::test]
6313async fn test_extra_newline_insertion(cx: &mut gpui::TestAppContext) {
6314    init_test(cx, |_| {});
6315
6316    let language = Arc::new(
6317        Language::new(
6318            LanguageConfig {
6319                brackets: BracketPairConfig {
6320                    pairs: vec![
6321                        BracketPair {
6322                            start: "{".to_string(),
6323                            end: "}".to_string(),
6324                            close: true,
6325                            newline: true,
6326                        },
6327                        BracketPair {
6328                            start: "/* ".to_string(),
6329                            end: " */".to_string(),
6330                            close: true,
6331                            newline: true,
6332                        },
6333                    ],
6334                    ..Default::default()
6335                },
6336                ..Default::default()
6337            },
6338            Some(tree_sitter_rust::language()),
6339        )
6340        .with_indents_query("")
6341        .unwrap(),
6342    );
6343
6344    let text = concat!(
6345        "{   }\n",     //
6346        "  x\n",       //
6347        "  /*   */\n", //
6348        "x\n",         //
6349        "{{} }\n",     //
6350    );
6351
6352    let buffer = cx.build_model(|cx| {
6353        Buffer::new(0, cx.entity_id().as_u64(), text).with_language(language, cx)
6354    });
6355    let buffer = cx.build_model(|cx| MultiBuffer::singleton(buffer, cx));
6356    let (view, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
6357    view.condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
6358        .await;
6359
6360    view.update(cx, |view, cx| {
6361        view.change_selections(None, cx, |s| {
6362            s.select_display_ranges([
6363                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 3),
6364                DisplayPoint::new(2, 5)..DisplayPoint::new(2, 5),
6365                DisplayPoint::new(4, 4)..DisplayPoint::new(4, 4),
6366            ])
6367        });
6368        view.newline(&Newline, cx);
6369
6370        assert_eq!(
6371            view.buffer().read(cx).read(cx).text(),
6372            concat!(
6373                "{ \n",    // Suppress rustfmt
6374                "\n",      //
6375                "}\n",     //
6376                "  x\n",   //
6377                "  /* \n", //
6378                "  \n",    //
6379                "  */\n",  //
6380                "x\n",     //
6381                "{{} \n",  //
6382                "}\n",     //
6383            )
6384        );
6385    });
6386}
6387
6388#[gpui::test]
6389fn test_highlighted_ranges(cx: &mut TestAppContext) {
6390    init_test(cx, |_| {});
6391
6392    let editor = cx.add_window(|cx| {
6393        let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
6394        build_editor(buffer.clone(), cx)
6395    });
6396
6397    editor.update(cx, |editor, cx| {
6398        struct Type1;
6399        struct Type2;
6400
6401        let buffer = editor.buffer.read(cx).snapshot(cx);
6402
6403        let anchor_range =
6404            |range: Range<Point>| buffer.anchor_after(range.start)..buffer.anchor_after(range.end);
6405
6406        editor.highlight_background::<Type1>(
6407            vec![
6408                anchor_range(Point::new(2, 1)..Point::new(2, 3)),
6409                anchor_range(Point::new(4, 2)..Point::new(4, 4)),
6410                anchor_range(Point::new(6, 3)..Point::new(6, 5)),
6411                anchor_range(Point::new(8, 4)..Point::new(8, 6)),
6412            ],
6413            |_| Hsla::red(),
6414            cx,
6415        );
6416        editor.highlight_background::<Type2>(
6417            vec![
6418                anchor_range(Point::new(3, 2)..Point::new(3, 5)),
6419                anchor_range(Point::new(5, 3)..Point::new(5, 6)),
6420                anchor_range(Point::new(7, 4)..Point::new(7, 7)),
6421                anchor_range(Point::new(9, 5)..Point::new(9, 8)),
6422            ],
6423            |_| Hsla::green(),
6424            cx,
6425        );
6426
6427        let snapshot = editor.snapshot(cx);
6428        let mut highlighted_ranges = editor.background_highlights_in_range(
6429            anchor_range(Point::new(3, 4)..Point::new(7, 4)),
6430            &snapshot,
6431            cx.theme().colors(),
6432        );
6433        // Enforce a consistent ordering based on color without relying on the ordering of the
6434        // highlight's `TypeId` which is non-executor.
6435        highlighted_ranges.sort_unstable_by_key(|(_, color)| *color);
6436        assert_eq!(
6437            highlighted_ranges,
6438            &[
6439                (
6440                    DisplayPoint::new(4, 2)..DisplayPoint::new(4, 4),
6441                    Hsla::red(),
6442                ),
6443                (
6444                    DisplayPoint::new(6, 3)..DisplayPoint::new(6, 5),
6445                    Hsla::red(),
6446                ),
6447                (
6448                    DisplayPoint::new(3, 2)..DisplayPoint::new(3, 5),
6449                    Hsla::green(),
6450                ),
6451                (
6452                    DisplayPoint::new(5, 3)..DisplayPoint::new(5, 6),
6453                    Hsla::green(),
6454                ),
6455            ]
6456        );
6457        assert_eq!(
6458            editor.background_highlights_in_range(
6459                anchor_range(Point::new(5, 6)..Point::new(6, 4)),
6460                &snapshot,
6461                cx.theme().colors(),
6462            ),
6463            &[(
6464                DisplayPoint::new(6, 3)..DisplayPoint::new(6, 5),
6465                Hsla::red(),
6466            )]
6467        );
6468    });
6469}
6470
6471// todo!(following)
6472#[gpui::test]
6473async fn test_following(cx: &mut gpui::TestAppContext) {
6474    init_test(cx, |_| {});
6475
6476    let fs = FakeFs::new(cx.executor());
6477    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
6478
6479    let buffer = project.update(cx, |project, cx| {
6480        let buffer = project
6481            .create_buffer(&sample_text(16, 8, 'a'), None, cx)
6482            .unwrap();
6483        cx.build_model(|cx| MultiBuffer::singleton(buffer, cx))
6484    });
6485    let leader = cx.add_window(|cx| build_editor(buffer.clone(), cx));
6486    let follower = cx.update(|cx| {
6487        cx.open_window(
6488            WindowOptions {
6489                bounds: WindowBounds::Fixed(Bounds::from_corners(
6490                    gpui::Point::new((0. as f64).into(), (0. as f64).into()),
6491                    gpui::Point::new((10. as f64).into(), (80. as f64).into()),
6492                )),
6493                ..Default::default()
6494            },
6495            |cx| cx.build_view(|cx| build_editor(buffer.clone(), cx)),
6496        )
6497    });
6498
6499    let is_still_following = Rc::new(RefCell::new(true));
6500    let follower_edit_event_count = Rc::new(RefCell::new(0));
6501    let pending_update = Rc::new(RefCell::new(None));
6502    follower.update(cx, {
6503        let update = pending_update.clone();
6504        let is_still_following = is_still_following.clone();
6505        let follower_edit_event_count = follower_edit_event_count.clone();
6506        |_, cx| {
6507            cx.subscribe(
6508                &leader.root_view(cx).unwrap(),
6509                move |_, leader, event, cx| {
6510                    leader
6511                        .read(cx)
6512                        .add_event_to_update_proto(event, &mut *update.borrow_mut(), cx);
6513                },
6514            )
6515            .detach();
6516
6517            cx.subscribe(
6518                &follower.root_view(cx).unwrap(),
6519                move |_, _, event: &EditorEvent, cx| {
6520                    if matches!(Editor::to_follow_event(event), Some(FollowEvent::Unfollow)) {
6521                        *is_still_following.borrow_mut() = false;
6522                    }
6523
6524                    if let EditorEvent::BufferEdited = event {
6525                        *follower_edit_event_count.borrow_mut() += 1;
6526                    }
6527                },
6528            )
6529            .detach();
6530        }
6531    });
6532
6533    // Update the selections only
6534    leader.update(cx, |leader, cx| {
6535        leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
6536    });
6537    follower
6538        .update(cx, |follower, cx| {
6539            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
6540        })
6541        .unwrap()
6542        .await
6543        .unwrap();
6544    follower.update(cx, |follower, cx| {
6545        assert_eq!(follower.selections.ranges(cx), vec![1..1]);
6546    });
6547    assert_eq!(*is_still_following.borrow(), true);
6548    assert_eq!(*follower_edit_event_count.borrow(), 0);
6549
6550    // Update the scroll position only
6551    leader.update(cx, |leader, cx| {
6552        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), cx);
6553    });
6554    follower
6555        .update(cx, |follower, cx| {
6556            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
6557        })
6558        .unwrap()
6559        .await
6560        .unwrap();
6561    assert_eq!(
6562        follower
6563            .update(cx, |follower, cx| follower.scroll_position(cx))
6564            .unwrap(),
6565        gpui::Point::new(1.5, 3.5)
6566    );
6567    assert_eq!(*is_still_following.borrow(), true);
6568    assert_eq!(*follower_edit_event_count.borrow(), 0);
6569
6570    // Update the selections and scroll position. The follower's scroll position is updated
6571    // via autoscroll, not via the leader's exact scroll position.
6572    leader.update(cx, |leader, cx| {
6573        leader.change_selections(None, cx, |s| s.select_ranges([0..0]));
6574        leader.request_autoscroll(Autoscroll::newest(), cx);
6575        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), cx);
6576    });
6577    follower
6578        .update(cx, |follower, cx| {
6579            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
6580        })
6581        .unwrap()
6582        .await
6583        .unwrap();
6584    follower.update(cx, |follower, cx| {
6585        assert_eq!(follower.scroll_position(cx), gpui::Point::new(1.5, 0.0));
6586        assert_eq!(follower.selections.ranges(cx), vec![0..0]);
6587    });
6588    assert_eq!(*is_still_following.borrow(), true);
6589
6590    // Creating a pending selection that precedes another selection
6591    leader.update(cx, |leader, cx| {
6592        leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
6593        leader.begin_selection(DisplayPoint::new(0, 0), true, 1, cx);
6594    });
6595    follower
6596        .update(cx, |follower, cx| {
6597            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
6598        })
6599        .unwrap()
6600        .await
6601        .unwrap();
6602    follower.update(cx, |follower, cx| {
6603        assert_eq!(follower.selections.ranges(cx), vec![0..0, 1..1]);
6604    });
6605    assert_eq!(*is_still_following.borrow(), true);
6606
6607    // Extend the pending selection so that it surrounds another selection
6608    leader.update(cx, |leader, cx| {
6609        leader.extend_selection(DisplayPoint::new(0, 2), 1, cx);
6610    });
6611    follower
6612        .update(cx, |follower, cx| {
6613            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
6614        })
6615        .unwrap()
6616        .await
6617        .unwrap();
6618    follower.update(cx, |follower, cx| {
6619        assert_eq!(follower.selections.ranges(cx), vec![0..2]);
6620    });
6621
6622    // Scrolling locally breaks the follow
6623    follower.update(cx, |follower, cx| {
6624        let top_anchor = follower.buffer().read(cx).read(cx).anchor_after(0);
6625        follower.set_scroll_anchor(
6626            ScrollAnchor {
6627                anchor: top_anchor,
6628                offset: gpui::Point::new(0.0, 0.5),
6629            },
6630            cx,
6631        );
6632    });
6633    assert_eq!(*is_still_following.borrow(), false);
6634}
6635
6636#[gpui::test]
6637async fn test_following_with_multiple_excerpts(cx: &mut gpui::TestAppContext) {
6638    init_test(cx, |_| {});
6639
6640    let fs = FakeFs::new(cx.executor());
6641    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
6642    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
6643    let pane = workspace
6644        .update(cx, |workspace, _| workspace.active_pane().clone())
6645        .unwrap();
6646
6647    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
6648
6649    let leader = pane.update(cx, |_, cx| {
6650        let multibuffer = cx.build_model(|_| MultiBuffer::new(0));
6651        cx.build_view(|cx| build_editor(multibuffer.clone(), cx))
6652    });
6653
6654    // Start following the editor when it has no excerpts.
6655    let mut state_message = leader.update(cx, |leader, cx| leader.to_state_proto(cx));
6656    let follower_1 = cx
6657        .update_window(*workspace.deref(), |_, cx| {
6658            Editor::from_state_proto(
6659                pane.clone(),
6660                workspace.root_view(cx).unwrap(),
6661                ViewId {
6662                    creator: Default::default(),
6663                    id: 0,
6664                },
6665                &mut state_message,
6666                cx,
6667            )
6668        })
6669        .unwrap()
6670        .unwrap()
6671        .await
6672        .unwrap();
6673
6674    let update_message = Rc::new(RefCell::new(None));
6675    follower_1.update(cx, {
6676        let update = update_message.clone();
6677        |_, cx| {
6678            cx.subscribe(&leader, move |_, leader, event, cx| {
6679                leader
6680                    .read(cx)
6681                    .add_event_to_update_proto(event, &mut *update.borrow_mut(), cx);
6682            })
6683            .detach();
6684        }
6685    });
6686
6687    let (buffer_1, buffer_2) = project.update(cx, |project, cx| {
6688        (
6689            project
6690                .create_buffer("abc\ndef\nghi\njkl\n", None, cx)
6691                .unwrap(),
6692            project
6693                .create_buffer("mno\npqr\nstu\nvwx\n", None, cx)
6694                .unwrap(),
6695        )
6696    });
6697
6698    // Insert some excerpts.
6699    leader.update(cx, |leader, cx| {
6700        leader.buffer.update(cx, |multibuffer, cx| {
6701            let excerpt_ids = multibuffer.push_excerpts(
6702                buffer_1.clone(),
6703                [
6704                    ExcerptRange {
6705                        context: 1..6,
6706                        primary: None,
6707                    },
6708                    ExcerptRange {
6709                        context: 12..15,
6710                        primary: None,
6711                    },
6712                    ExcerptRange {
6713                        context: 0..3,
6714                        primary: None,
6715                    },
6716                ],
6717                cx,
6718            );
6719            multibuffer.insert_excerpts_after(
6720                excerpt_ids[0],
6721                buffer_2.clone(),
6722                [
6723                    ExcerptRange {
6724                        context: 8..12,
6725                        primary: None,
6726                    },
6727                    ExcerptRange {
6728                        context: 0..6,
6729                        primary: None,
6730                    },
6731                ],
6732                cx,
6733            );
6734        });
6735    });
6736
6737    // Apply the update of adding the excerpts.
6738    follower_1
6739        .update(cx, |follower, cx| {
6740            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
6741        })
6742        .await
6743        .unwrap();
6744    assert_eq!(
6745        follower_1.update(cx, |editor, cx| editor.text(cx)),
6746        leader.update(cx, |editor, cx| editor.text(cx))
6747    );
6748    update_message.borrow_mut().take();
6749
6750    // Start following separately after it already has excerpts.
6751    let mut state_message = leader.update(cx, |leader, cx| leader.to_state_proto(cx));
6752    let follower_2 = cx
6753        .update_window(*workspace.deref(), |_, cx| {
6754            Editor::from_state_proto(
6755                pane.clone(),
6756                workspace.root_view(cx).unwrap().clone(),
6757                ViewId {
6758                    creator: Default::default(),
6759                    id: 0,
6760                },
6761                &mut state_message,
6762                cx,
6763            )
6764        })
6765        .unwrap()
6766        .unwrap()
6767        .await
6768        .unwrap();
6769    assert_eq!(
6770        follower_2.update(cx, |editor, cx| editor.text(cx)),
6771        leader.update(cx, |editor, cx| editor.text(cx))
6772    );
6773
6774    // Remove some excerpts.
6775    leader.update(cx, |leader, cx| {
6776        leader.buffer.update(cx, |multibuffer, cx| {
6777            let excerpt_ids = multibuffer.excerpt_ids();
6778            multibuffer.remove_excerpts([excerpt_ids[1], excerpt_ids[2]], cx);
6779            multibuffer.remove_excerpts([excerpt_ids[0]], cx);
6780        });
6781    });
6782
6783    // Apply the update of removing the excerpts.
6784    follower_1
6785        .update(cx, |follower, cx| {
6786            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
6787        })
6788        .await
6789        .unwrap();
6790    follower_2
6791        .update(cx, |follower, cx| {
6792            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
6793        })
6794        .await
6795        .unwrap();
6796    update_message.borrow_mut().take();
6797    assert_eq!(
6798        follower_1.update(cx, |editor, cx| editor.text(cx)),
6799        leader.update(cx, |editor, cx| editor.text(cx))
6800    );
6801}
6802
6803#[gpui::test]
6804async fn go_to_prev_overlapping_diagnostic(
6805    executor: BackgroundExecutor,
6806    cx: &mut gpui::TestAppContext,
6807) {
6808    init_test(cx, |_| {});
6809
6810    let mut cx = EditorTestContext::new(cx).await;
6811    let project = cx.update_editor(|editor, _| editor.project.clone().unwrap());
6812
6813    cx.set_state(indoc! {"
6814        ˇfn func(abc def: i32) -> u32 {
6815        }
6816    "});
6817
6818    cx.update(|cx| {
6819        project.update(cx, |project, cx| {
6820            project
6821                .update_diagnostics(
6822                    LanguageServerId(0),
6823                    lsp::PublishDiagnosticsParams {
6824                        uri: lsp::Url::from_file_path("/root/file").unwrap(),
6825                        version: None,
6826                        diagnostics: vec![
6827                            lsp::Diagnostic {
6828                                range: lsp::Range::new(
6829                                    lsp::Position::new(0, 11),
6830                                    lsp::Position::new(0, 12),
6831                                ),
6832                                severity: Some(lsp::DiagnosticSeverity::ERROR),
6833                                ..Default::default()
6834                            },
6835                            lsp::Diagnostic {
6836                                range: lsp::Range::new(
6837                                    lsp::Position::new(0, 12),
6838                                    lsp::Position::new(0, 15),
6839                                ),
6840                                severity: Some(lsp::DiagnosticSeverity::ERROR),
6841                                ..Default::default()
6842                            },
6843                            lsp::Diagnostic {
6844                                range: lsp::Range::new(
6845                                    lsp::Position::new(0, 25),
6846                                    lsp::Position::new(0, 28),
6847                                ),
6848                                severity: Some(lsp::DiagnosticSeverity::ERROR),
6849                                ..Default::default()
6850                            },
6851                        ],
6852                    },
6853                    &[],
6854                    cx,
6855                )
6856                .unwrap()
6857        });
6858    });
6859
6860    executor.run_until_parked();
6861
6862    cx.update_editor(|editor, cx| {
6863        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
6864    });
6865
6866    cx.assert_editor_state(indoc! {"
6867        fn func(abc def: i32) -> ˇu32 {
6868        }
6869    "});
6870
6871    cx.update_editor(|editor, cx| {
6872        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
6873    });
6874
6875    cx.assert_editor_state(indoc! {"
6876        fn func(abc ˇdef: i32) -> u32 {
6877        }
6878    "});
6879
6880    cx.update_editor(|editor, cx| {
6881        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
6882    });
6883
6884    cx.assert_editor_state(indoc! {"
6885        fn func(abcˇ def: i32) -> u32 {
6886        }
6887    "});
6888
6889    cx.update_editor(|editor, cx| {
6890        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
6891    });
6892
6893    cx.assert_editor_state(indoc! {"
6894        fn func(abc def: i32) -> ˇu32 {
6895        }
6896    "});
6897}
6898
6899#[gpui::test]
6900async fn go_to_hunk(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
6901    init_test(cx, |_| {});
6902
6903    let mut cx = EditorTestContext::new(cx).await;
6904
6905    let diff_base = r#"
6906        use some::mod;
6907
6908        const A: u32 = 42;
6909
6910        fn main() {
6911            println!("hello");
6912
6913            println!("world");
6914        }
6915        "#
6916    .unindent();
6917
6918    // Edits are modified, removed, modified, added
6919    cx.set_state(
6920        &r#"
6921        use some::modified;
6922
6923        ˇ
6924        fn main() {
6925            println!("hello there");
6926
6927            println!("around the");
6928            println!("world");
6929        }
6930        "#
6931        .unindent(),
6932    );
6933
6934    cx.set_diff_base(Some(&diff_base));
6935    executor.run_until_parked();
6936
6937    cx.update_editor(|editor, cx| {
6938        //Wrap around the bottom of the buffer
6939        for _ in 0..3 {
6940            editor.go_to_hunk(&GoToHunk, cx);
6941        }
6942    });
6943
6944    cx.assert_editor_state(
6945        &r#"
6946        ˇuse some::modified;
6947
6948
6949        fn main() {
6950            println!("hello there");
6951
6952            println!("around the");
6953            println!("world");
6954        }
6955        "#
6956        .unindent(),
6957    );
6958
6959    cx.update_editor(|editor, cx| {
6960        //Wrap around the top of the buffer
6961        for _ in 0..2 {
6962            editor.go_to_prev_hunk(&GoToPrevHunk, cx);
6963        }
6964    });
6965
6966    cx.assert_editor_state(
6967        &r#"
6968        use some::modified;
6969
6970
6971        fn main() {
6972        ˇ    println!("hello there");
6973
6974            println!("around the");
6975            println!("world");
6976        }
6977        "#
6978        .unindent(),
6979    );
6980
6981    cx.update_editor(|editor, cx| {
6982        editor.go_to_prev_hunk(&GoToPrevHunk, cx);
6983    });
6984
6985    cx.assert_editor_state(
6986        &r#"
6987        use some::modified;
6988
6989        ˇ
6990        fn main() {
6991            println!("hello there");
6992
6993            println!("around the");
6994            println!("world");
6995        }
6996        "#
6997        .unindent(),
6998    );
6999
7000    cx.update_editor(|editor, cx| {
7001        for _ in 0..3 {
7002            editor.go_to_prev_hunk(&GoToPrevHunk, cx);
7003        }
7004    });
7005
7006    cx.assert_editor_state(
7007        &r#"
7008        use some::modified;
7009
7010
7011        fn main() {
7012        ˇ    println!("hello there");
7013
7014            println!("around the");
7015            println!("world");
7016        }
7017        "#
7018        .unindent(),
7019    );
7020
7021    cx.update_editor(|editor, cx| {
7022        editor.fold(&Fold, cx);
7023
7024        //Make sure that the fold only gets one hunk
7025        for _ in 0..4 {
7026            editor.go_to_hunk(&GoToHunk, cx);
7027        }
7028    });
7029
7030    cx.assert_editor_state(
7031        &r#"
7032        ˇuse some::modified;
7033
7034
7035        fn main() {
7036            println!("hello there");
7037
7038            println!("around the");
7039            println!("world");
7040        }
7041        "#
7042        .unindent(),
7043    );
7044}
7045
7046#[test]
7047fn test_split_words() {
7048    fn split<'a>(text: &'a str) -> Vec<&'a str> {
7049        split_words(text).collect()
7050    }
7051
7052    assert_eq!(split("HelloWorld"), &["Hello", "World"]);
7053    assert_eq!(split("hello_world"), &["hello_", "world"]);
7054    assert_eq!(split("_hello_world_"), &["_", "hello_", "world_"]);
7055    assert_eq!(split("Hello_World"), &["Hello_", "World"]);
7056    assert_eq!(split("helloWOrld"), &["hello", "WOrld"]);
7057    assert_eq!(split("helloworld"), &["helloworld"]);
7058}
7059
7060#[gpui::test]
7061async fn test_move_to_enclosing_bracket(cx: &mut gpui::TestAppContext) {
7062    init_test(cx, |_| {});
7063
7064    let mut cx = EditorLspTestContext::new_typescript(Default::default(), cx).await;
7065    let mut assert = |before, after| {
7066        let _state_context = cx.set_state(before);
7067        cx.update_editor(|editor, cx| {
7068            editor.move_to_enclosing_bracket(&MoveToEnclosingBracket, cx)
7069        });
7070        cx.assert_editor_state(after);
7071    };
7072
7073    // Outside bracket jumps to outside of matching bracket
7074    assert("console.logˇ(var);", "console.log(var)ˇ;");
7075    assert("console.log(var)ˇ;", "console.logˇ(var);");
7076
7077    // Inside bracket jumps to inside of matching bracket
7078    assert("console.log(ˇvar);", "console.log(varˇ);");
7079    assert("console.log(varˇ);", "console.log(ˇvar);");
7080
7081    // When outside a bracket and inside, favor jumping to the inside bracket
7082    assert(
7083        "console.log('foo', [1, 2, 3]ˇ);",
7084        "console.log(ˇ'foo', [1, 2, 3]);",
7085    );
7086    assert(
7087        "console.log(ˇ'foo', [1, 2, 3]);",
7088        "console.log('foo', [1, 2, 3]ˇ);",
7089    );
7090
7091    // Bias forward if two options are equally likely
7092    assert(
7093        "let result = curried_fun()ˇ();",
7094        "let result = curried_fun()()ˇ;",
7095    );
7096
7097    // If directly adjacent to a smaller pair but inside a larger (not adjacent), pick the smaller
7098    assert(
7099        indoc! {"
7100            function test() {
7101                console.log('test')ˇ
7102            }"},
7103        indoc! {"
7104            function test() {
7105                console.logˇ('test')
7106            }"},
7107    );
7108}
7109
7110// todo!(completions)
7111#[gpui::test(iterations = 10)]
7112async fn test_copilot(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
7113    // flaky
7114    init_test(cx, |_| {});
7115
7116    let (copilot, copilot_lsp) = Copilot::fake(cx);
7117    cx.update(|cx| cx.set_global(copilot));
7118    let mut cx = EditorLspTestContext::new_rust(
7119        lsp::ServerCapabilities {
7120            completion_provider: Some(lsp::CompletionOptions {
7121                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
7122                ..Default::default()
7123            }),
7124            ..Default::default()
7125        },
7126        cx,
7127    )
7128    .await;
7129
7130    // When inserting, ensure autocompletion is favored over Copilot suggestions.
7131    cx.set_state(indoc! {"
7132        oneˇ
7133        two
7134        three
7135    "});
7136    cx.simulate_keystroke(".");
7137    let _ = handle_completion_request(
7138        &mut cx,
7139        indoc! {"
7140            one.|<>
7141            two
7142            three
7143        "},
7144        vec!["completion_a", "completion_b"],
7145    );
7146    handle_copilot_completion_request(
7147        &copilot_lsp,
7148        vec![copilot::request::Completion {
7149            text: "one.copilot1".into(),
7150            range: lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 4)),
7151            ..Default::default()
7152        }],
7153        vec![],
7154    );
7155    executor.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
7156    cx.update_editor(|editor, cx| {
7157        assert!(editor.context_menu_visible());
7158        assert!(!editor.has_active_copilot_suggestion(cx));
7159
7160        // Confirming a completion inserts it and hides the context menu, without showing
7161        // the copilot suggestion afterwards.
7162        editor
7163            .confirm_completion(&Default::default(), cx)
7164            .unwrap()
7165            .detach();
7166        assert!(!editor.context_menu_visible());
7167        assert!(!editor.has_active_copilot_suggestion(cx));
7168        assert_eq!(editor.text(cx), "one.completion_a\ntwo\nthree\n");
7169        assert_eq!(editor.display_text(cx), "one.completion_a\ntwo\nthree\n");
7170    });
7171
7172    // Ensure Copilot suggestions are shown right away if no autocompletion is available.
7173    cx.set_state(indoc! {"
7174        oneˇ
7175        two
7176        three
7177    "});
7178    cx.simulate_keystroke(".");
7179    let _ = handle_completion_request(
7180        &mut cx,
7181        indoc! {"
7182            one.|<>
7183            two
7184            three
7185        "},
7186        vec![],
7187    );
7188    handle_copilot_completion_request(
7189        &copilot_lsp,
7190        vec![copilot::request::Completion {
7191            text: "one.copilot1".into(),
7192            range: lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 4)),
7193            ..Default::default()
7194        }],
7195        vec![],
7196    );
7197    executor.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
7198    cx.update_editor(|editor, cx| {
7199        assert!(!editor.context_menu_visible());
7200        assert!(editor.has_active_copilot_suggestion(cx));
7201        assert_eq!(editor.display_text(cx), "one.copilot1\ntwo\nthree\n");
7202        assert_eq!(editor.text(cx), "one.\ntwo\nthree\n");
7203    });
7204
7205    // Reset editor, and ensure autocompletion is still favored over Copilot suggestions.
7206    cx.set_state(indoc! {"
7207        oneˇ
7208        two
7209        three
7210    "});
7211    cx.simulate_keystroke(".");
7212    let _ = handle_completion_request(
7213        &mut cx,
7214        indoc! {"
7215            one.|<>
7216            two
7217            three
7218        "},
7219        vec!["completion_a", "completion_b"],
7220    );
7221    handle_copilot_completion_request(
7222        &copilot_lsp,
7223        vec![copilot::request::Completion {
7224            text: "one.copilot1".into(),
7225            range: lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 4)),
7226            ..Default::default()
7227        }],
7228        vec![],
7229    );
7230    executor.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
7231    cx.update_editor(|editor, cx| {
7232        assert!(editor.context_menu_visible());
7233        assert!(!editor.has_active_copilot_suggestion(cx));
7234
7235        // When hiding the context menu, the Copilot suggestion becomes visible.
7236        editor.hide_context_menu(cx);
7237        assert!(!editor.context_menu_visible());
7238        assert!(editor.has_active_copilot_suggestion(cx));
7239        assert_eq!(editor.display_text(cx), "one.copilot1\ntwo\nthree\n");
7240        assert_eq!(editor.text(cx), "one.\ntwo\nthree\n");
7241    });
7242
7243    // Ensure existing completion is interpolated when inserting again.
7244    cx.simulate_keystroke("c");
7245    executor.run_until_parked();
7246    cx.update_editor(|editor, cx| {
7247        assert!(!editor.context_menu_visible());
7248        assert!(editor.has_active_copilot_suggestion(cx));
7249        assert_eq!(editor.display_text(cx), "one.copilot1\ntwo\nthree\n");
7250        assert_eq!(editor.text(cx), "one.c\ntwo\nthree\n");
7251    });
7252
7253    // After debouncing, new Copilot completions should be requested.
7254    handle_copilot_completion_request(
7255        &copilot_lsp,
7256        vec![copilot::request::Completion {
7257            text: "one.copilot2".into(),
7258            range: lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 5)),
7259            ..Default::default()
7260        }],
7261        vec![],
7262    );
7263    executor.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
7264    cx.update_editor(|editor, cx| {
7265        assert!(!editor.context_menu_visible());
7266        assert!(editor.has_active_copilot_suggestion(cx));
7267        assert_eq!(editor.display_text(cx), "one.copilot2\ntwo\nthree\n");
7268        assert_eq!(editor.text(cx), "one.c\ntwo\nthree\n");
7269
7270        // Canceling should remove the active Copilot suggestion.
7271        editor.cancel(&Default::default(), cx);
7272        assert!(!editor.has_active_copilot_suggestion(cx));
7273        assert_eq!(editor.display_text(cx), "one.c\ntwo\nthree\n");
7274        assert_eq!(editor.text(cx), "one.c\ntwo\nthree\n");
7275
7276        // After canceling, tabbing shouldn't insert the previously shown suggestion.
7277        editor.tab(&Default::default(), cx);
7278        assert!(!editor.has_active_copilot_suggestion(cx));
7279        assert_eq!(editor.display_text(cx), "one.c   \ntwo\nthree\n");
7280        assert_eq!(editor.text(cx), "one.c   \ntwo\nthree\n");
7281
7282        // When undoing the previously active suggestion is shown again.
7283        editor.undo(&Default::default(), cx);
7284        assert!(editor.has_active_copilot_suggestion(cx));
7285        assert_eq!(editor.display_text(cx), "one.copilot2\ntwo\nthree\n");
7286        assert_eq!(editor.text(cx), "one.c\ntwo\nthree\n");
7287    });
7288
7289    // If an edit occurs outside of this editor, the suggestion is still correctly interpolated.
7290    cx.update_buffer(|buffer, cx| buffer.edit([(5..5, "o")], None, cx));
7291    cx.update_editor(|editor, cx| {
7292        assert!(editor.has_active_copilot_suggestion(cx));
7293        assert_eq!(editor.display_text(cx), "one.copilot2\ntwo\nthree\n");
7294        assert_eq!(editor.text(cx), "one.co\ntwo\nthree\n");
7295
7296        // Tabbing when there is an active suggestion inserts it.
7297        editor.tab(&Default::default(), cx);
7298        assert!(!editor.has_active_copilot_suggestion(cx));
7299        assert_eq!(editor.display_text(cx), "one.copilot2\ntwo\nthree\n");
7300        assert_eq!(editor.text(cx), "one.copilot2\ntwo\nthree\n");
7301
7302        // When undoing the previously active suggestion is shown again.
7303        editor.undo(&Default::default(), cx);
7304        assert!(editor.has_active_copilot_suggestion(cx));
7305        assert_eq!(editor.display_text(cx), "one.copilot2\ntwo\nthree\n");
7306        assert_eq!(editor.text(cx), "one.co\ntwo\nthree\n");
7307
7308        // Hide suggestion.
7309        editor.cancel(&Default::default(), cx);
7310        assert!(!editor.has_active_copilot_suggestion(cx));
7311        assert_eq!(editor.display_text(cx), "one.co\ntwo\nthree\n");
7312        assert_eq!(editor.text(cx), "one.co\ntwo\nthree\n");
7313    });
7314
7315    // If an edit occurs outside of this editor but no suggestion is being shown,
7316    // we won't make it visible.
7317    cx.update_buffer(|buffer, cx| buffer.edit([(6..6, "p")], None, cx));
7318    cx.update_editor(|editor, cx| {
7319        assert!(!editor.has_active_copilot_suggestion(cx));
7320        assert_eq!(editor.display_text(cx), "one.cop\ntwo\nthree\n");
7321        assert_eq!(editor.text(cx), "one.cop\ntwo\nthree\n");
7322    });
7323
7324    // Reset the editor to verify how suggestions behave when tabbing on leading indentation.
7325    cx.update_editor(|editor, cx| {
7326        editor.set_text("fn foo() {\n  \n}", cx);
7327        editor.change_selections(None, cx, |s| {
7328            s.select_ranges([Point::new(1, 2)..Point::new(1, 2)])
7329        });
7330    });
7331    handle_copilot_completion_request(
7332        &copilot_lsp,
7333        vec![copilot::request::Completion {
7334            text: "    let x = 4;".into(),
7335            range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 2)),
7336            ..Default::default()
7337        }],
7338        vec![],
7339    );
7340
7341    cx.update_editor(|editor, cx| editor.next_copilot_suggestion(&Default::default(), cx));
7342    executor.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
7343    cx.update_editor(|editor, cx| {
7344        assert!(editor.has_active_copilot_suggestion(cx));
7345        assert_eq!(editor.display_text(cx), "fn foo() {\n    let x = 4;\n}");
7346        assert_eq!(editor.text(cx), "fn foo() {\n  \n}");
7347
7348        // Tabbing inside of leading whitespace inserts indentation without accepting the suggestion.
7349        editor.tab(&Default::default(), cx);
7350        assert!(editor.has_active_copilot_suggestion(cx));
7351        assert_eq!(editor.text(cx), "fn foo() {\n    \n}");
7352        assert_eq!(editor.display_text(cx), "fn foo() {\n    let x = 4;\n}");
7353
7354        // Tabbing again accepts the suggestion.
7355        editor.tab(&Default::default(), cx);
7356        assert!(!editor.has_active_copilot_suggestion(cx));
7357        assert_eq!(editor.text(cx), "fn foo() {\n    let x = 4;\n}");
7358        assert_eq!(editor.display_text(cx), "fn foo() {\n    let x = 4;\n}");
7359    });
7360}
7361
7362#[gpui::test]
7363async fn test_copilot_completion_invalidation(
7364    executor: BackgroundExecutor,
7365    cx: &mut gpui::TestAppContext,
7366) {
7367    init_test(cx, |_| {});
7368
7369    let (copilot, copilot_lsp) = Copilot::fake(cx);
7370    cx.update(|cx| cx.set_global(copilot));
7371    let mut cx = EditorLspTestContext::new_rust(
7372        lsp::ServerCapabilities {
7373            completion_provider: Some(lsp::CompletionOptions {
7374                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
7375                ..Default::default()
7376            }),
7377            ..Default::default()
7378        },
7379        cx,
7380    )
7381    .await;
7382
7383    cx.set_state(indoc! {"
7384        one
7385        twˇ
7386        three
7387    "});
7388
7389    handle_copilot_completion_request(
7390        &copilot_lsp,
7391        vec![copilot::request::Completion {
7392            text: "two.foo()".into(),
7393            range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 2)),
7394            ..Default::default()
7395        }],
7396        vec![],
7397    );
7398    cx.update_editor(|editor, cx| editor.next_copilot_suggestion(&Default::default(), cx));
7399    executor.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
7400    cx.update_editor(|editor, cx| {
7401        assert!(editor.has_active_copilot_suggestion(cx));
7402        assert_eq!(editor.display_text(cx), "one\ntwo.foo()\nthree\n");
7403        assert_eq!(editor.text(cx), "one\ntw\nthree\n");
7404
7405        editor.backspace(&Default::default(), cx);
7406        assert!(editor.has_active_copilot_suggestion(cx));
7407        assert_eq!(editor.display_text(cx), "one\ntwo.foo()\nthree\n");
7408        assert_eq!(editor.text(cx), "one\nt\nthree\n");
7409
7410        editor.backspace(&Default::default(), cx);
7411        assert!(editor.has_active_copilot_suggestion(cx));
7412        assert_eq!(editor.display_text(cx), "one\ntwo.foo()\nthree\n");
7413        assert_eq!(editor.text(cx), "one\n\nthree\n");
7414
7415        // Deleting across the original suggestion range invalidates it.
7416        editor.backspace(&Default::default(), cx);
7417        assert!(!editor.has_active_copilot_suggestion(cx));
7418        assert_eq!(editor.display_text(cx), "one\nthree\n");
7419        assert_eq!(editor.text(cx), "one\nthree\n");
7420
7421        // Undoing the deletion restores the suggestion.
7422        editor.undo(&Default::default(), cx);
7423        assert!(editor.has_active_copilot_suggestion(cx));
7424        assert_eq!(editor.display_text(cx), "one\ntwo.foo()\nthree\n");
7425        assert_eq!(editor.text(cx), "one\n\nthree\n");
7426    });
7427}
7428
7429#[gpui::test]
7430async fn test_copilot_multibuffer(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
7431    init_test(cx, |_| {});
7432
7433    let (copilot, copilot_lsp) = Copilot::fake(cx);
7434    cx.update(|cx| cx.set_global(copilot));
7435
7436    let buffer_1 = cx.build_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), "a = 1\nb = 2\n"));
7437    let buffer_2 = cx.build_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), "c = 3\nd = 4\n"));
7438    let multibuffer = cx.build_model(|cx| {
7439        let mut multibuffer = MultiBuffer::new(0);
7440        multibuffer.push_excerpts(
7441            buffer_1.clone(),
7442            [ExcerptRange {
7443                context: Point::new(0, 0)..Point::new(2, 0),
7444                primary: None,
7445            }],
7446            cx,
7447        );
7448        multibuffer.push_excerpts(
7449            buffer_2.clone(),
7450            [ExcerptRange {
7451                context: Point::new(0, 0)..Point::new(2, 0),
7452                primary: None,
7453            }],
7454            cx,
7455        );
7456        multibuffer
7457    });
7458    let editor = cx.add_window(|cx| build_editor(multibuffer, cx));
7459
7460    handle_copilot_completion_request(
7461        &copilot_lsp,
7462        vec![copilot::request::Completion {
7463            text: "b = 2 + a".into(),
7464            range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 5)),
7465            ..Default::default()
7466        }],
7467        vec![],
7468    );
7469    editor.update(cx, |editor, cx| {
7470        // Ensure copilot suggestions are shown for the first excerpt.
7471        editor.change_selections(None, cx, |s| {
7472            s.select_ranges([Point::new(1, 5)..Point::new(1, 5)])
7473        });
7474        editor.next_copilot_suggestion(&Default::default(), cx);
7475    });
7476    executor.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
7477    editor.update(cx, |editor, cx| {
7478        assert!(editor.has_active_copilot_suggestion(cx));
7479        assert_eq!(
7480            editor.display_text(cx),
7481            "\n\na = 1\nb = 2 + a\n\n\n\nc = 3\nd = 4\n"
7482        );
7483        assert_eq!(editor.text(cx), "a = 1\nb = 2\n\nc = 3\nd = 4\n");
7484    });
7485
7486    handle_copilot_completion_request(
7487        &copilot_lsp,
7488        vec![copilot::request::Completion {
7489            text: "d = 4 + c".into(),
7490            range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 6)),
7491            ..Default::default()
7492        }],
7493        vec![],
7494    );
7495    editor.update(cx, |editor, cx| {
7496        // Move to another excerpt, ensuring the suggestion gets cleared.
7497        editor.change_selections(None, cx, |s| {
7498            s.select_ranges([Point::new(4, 5)..Point::new(4, 5)])
7499        });
7500        assert!(!editor.has_active_copilot_suggestion(cx));
7501        assert_eq!(
7502            editor.display_text(cx),
7503            "\n\na = 1\nb = 2\n\n\n\nc = 3\nd = 4\n"
7504        );
7505        assert_eq!(editor.text(cx), "a = 1\nb = 2\n\nc = 3\nd = 4\n");
7506
7507        // Type a character, ensuring we don't even try to interpolate the previous suggestion.
7508        editor.handle_input(" ", cx);
7509        assert!(!editor.has_active_copilot_suggestion(cx));
7510        assert_eq!(
7511            editor.display_text(cx),
7512            "\n\na = 1\nb = 2\n\n\n\nc = 3\nd = 4 \n"
7513        );
7514        assert_eq!(editor.text(cx), "a = 1\nb = 2\n\nc = 3\nd = 4 \n");
7515    });
7516
7517    // Ensure the new suggestion is displayed when the debounce timeout expires.
7518    executor.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
7519    editor.update(cx, |editor, cx| {
7520        assert!(editor.has_active_copilot_suggestion(cx));
7521        assert_eq!(
7522            editor.display_text(cx),
7523            "\n\na = 1\nb = 2\n\n\n\nc = 3\nd = 4 + c\n"
7524        );
7525        assert_eq!(editor.text(cx), "a = 1\nb = 2\n\nc = 3\nd = 4 \n");
7526    });
7527}
7528
7529#[gpui::test]
7530async fn test_copilot_disabled_globs(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
7531    init_test(cx, |settings| {
7532        settings
7533            .copilot
7534            .get_or_insert(Default::default())
7535            .disabled_globs = Some(vec![".env*".to_string()]);
7536    });
7537
7538    let (copilot, copilot_lsp) = Copilot::fake(cx);
7539    cx.update(|cx| cx.set_global(copilot));
7540
7541    let fs = FakeFs::new(cx.executor());
7542    fs.insert_tree(
7543        "/test",
7544        json!({
7545            ".env": "SECRET=something\n",
7546            "README.md": "hello\n"
7547        }),
7548    )
7549    .await;
7550    let project = Project::test(fs, ["/test".as_ref()], cx).await;
7551
7552    let private_buffer = project
7553        .update(cx, |project, cx| {
7554            project.open_local_buffer("/test/.env", cx)
7555        })
7556        .await
7557        .unwrap();
7558    let public_buffer = project
7559        .update(cx, |project, cx| {
7560            project.open_local_buffer("/test/README.md", cx)
7561        })
7562        .await
7563        .unwrap();
7564
7565    let multibuffer = cx.build_model(|cx| {
7566        let mut multibuffer = MultiBuffer::new(0);
7567        multibuffer.push_excerpts(
7568            private_buffer.clone(),
7569            [ExcerptRange {
7570                context: Point::new(0, 0)..Point::new(1, 0),
7571                primary: None,
7572            }],
7573            cx,
7574        );
7575        multibuffer.push_excerpts(
7576            public_buffer.clone(),
7577            [ExcerptRange {
7578                context: Point::new(0, 0)..Point::new(1, 0),
7579                primary: None,
7580            }],
7581            cx,
7582        );
7583        multibuffer
7584    });
7585    let editor = cx.add_window(|cx| build_editor(multibuffer, cx));
7586
7587    let mut copilot_requests = copilot_lsp
7588        .handle_request::<copilot::request::GetCompletions, _, _>(move |_params, _cx| async move {
7589            Ok(copilot::request::GetCompletionsResult {
7590                completions: vec![copilot::request::Completion {
7591                    text: "next line".into(),
7592                    range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 0)),
7593                    ..Default::default()
7594                }],
7595            })
7596        });
7597
7598    editor.update(cx, |editor, cx| {
7599        editor.change_selections(None, cx, |selections| {
7600            selections.select_ranges([Point::new(0, 0)..Point::new(0, 0)])
7601        });
7602        editor.next_copilot_suggestion(&Default::default(), cx);
7603    });
7604
7605    executor.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
7606    assert!(copilot_requests.try_next().is_err());
7607
7608    editor.update(cx, |editor, cx| {
7609        editor.change_selections(None, cx, |s| {
7610            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
7611        });
7612        editor.next_copilot_suggestion(&Default::default(), cx);
7613    });
7614
7615    executor.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
7616    assert!(copilot_requests.try_next().is_ok());
7617}
7618
7619#[gpui::test]
7620async fn test_on_type_formatting_not_triggered(cx: &mut gpui::TestAppContext) {
7621    init_test(cx, |_| {});
7622
7623    let mut language = Language::new(
7624        LanguageConfig {
7625            name: "Rust".into(),
7626            path_suffixes: vec!["rs".to_string()],
7627            brackets: BracketPairConfig {
7628                pairs: vec![BracketPair {
7629                    start: "{".to_string(),
7630                    end: "}".to_string(),
7631                    close: true,
7632                    newline: true,
7633                }],
7634                disabled_scopes_by_bracket_ix: Vec::new(),
7635            },
7636            ..Default::default()
7637        },
7638        Some(tree_sitter_rust::language()),
7639    );
7640    let mut fake_servers = language
7641        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
7642            capabilities: lsp::ServerCapabilities {
7643                document_on_type_formatting_provider: Some(lsp::DocumentOnTypeFormattingOptions {
7644                    first_trigger_character: "{".to_string(),
7645                    more_trigger_character: None,
7646                }),
7647                ..Default::default()
7648            },
7649            ..Default::default()
7650        }))
7651        .await;
7652
7653    let fs = FakeFs::new(cx.executor());
7654    fs.insert_tree(
7655        "/a",
7656        json!({
7657            "main.rs": "fn main() { let a = 5; }",
7658            "other.rs": "// Test file",
7659        }),
7660    )
7661    .await;
7662    let project = Project::test(fs, ["/a".as_ref()], cx).await;
7663    project.update(cx, |project, _| project.languages().add(Arc::new(language)));
7664    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
7665
7666    let cx = &mut VisualTestContext::from_window(*workspace, cx);
7667
7668    let worktree_id = workspace
7669        .update(cx, |workspace, cx| {
7670            workspace.project().update(cx, |project, cx| {
7671                project.worktrees().next().unwrap().read(cx).id()
7672            })
7673        })
7674        .unwrap();
7675
7676    let buffer = project
7677        .update(cx, |project, cx| {
7678            project.open_local_buffer("/a/main.rs", cx)
7679        })
7680        .await
7681        .unwrap();
7682    cx.executor().run_until_parked();
7683    cx.executor().start_waiting();
7684    let fake_server = fake_servers.next().await.unwrap();
7685    let editor_handle = workspace
7686        .update(cx, |workspace, cx| {
7687            workspace.open_path((worktree_id, "main.rs"), None, true, cx)
7688        })
7689        .unwrap()
7690        .await
7691        .unwrap()
7692        .downcast::<Editor>()
7693        .unwrap();
7694
7695    fake_server.handle_request::<lsp::request::OnTypeFormatting, _, _>(|params, _| async move {
7696        assert_eq!(
7697            params.text_document_position.text_document.uri,
7698            lsp::Url::from_file_path("/a/main.rs").unwrap(),
7699        );
7700        assert_eq!(
7701            params.text_document_position.position,
7702            lsp::Position::new(0, 21),
7703        );
7704
7705        Ok(Some(vec![lsp::TextEdit {
7706            new_text: "]".to_string(),
7707            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
7708        }]))
7709    });
7710
7711    editor_handle.update(cx, |editor, cx| {
7712        editor.focus(cx);
7713        editor.change_selections(None, cx, |s| {
7714            s.select_ranges([Point::new(0, 21)..Point::new(0, 20)])
7715        });
7716        editor.handle_input("{", cx);
7717    });
7718
7719    cx.executor().run_until_parked();
7720
7721    buffer.update(cx, |buffer, _| {
7722        assert_eq!(
7723            buffer.text(),
7724            "fn main() { let a = {5}; }",
7725            "No extra braces from on type formatting should appear in the buffer"
7726        )
7727    });
7728}
7729
7730#[gpui::test]
7731async fn test_language_server_restart_due_to_settings_change(cx: &mut gpui::TestAppContext) {
7732    init_test(cx, |_| {});
7733
7734    let language_name: Arc<str> = "Rust".into();
7735    let mut language = Language::new(
7736        LanguageConfig {
7737            name: Arc::clone(&language_name),
7738            path_suffixes: vec!["rs".to_string()],
7739            ..Default::default()
7740        },
7741        Some(tree_sitter_rust::language()),
7742    );
7743
7744    let server_restarts = Arc::new(AtomicUsize::new(0));
7745    let closure_restarts = Arc::clone(&server_restarts);
7746    let language_server_name = "test language server";
7747    let mut fake_servers = language
7748        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
7749            name: language_server_name,
7750            initialization_options: Some(json!({
7751                "testOptionValue": true
7752            })),
7753            initializer: Some(Box::new(move |fake_server| {
7754                let task_restarts = Arc::clone(&closure_restarts);
7755                fake_server.handle_request::<lsp::request::Shutdown, _, _>(move |_, _| {
7756                    task_restarts.fetch_add(1, atomic::Ordering::Release);
7757                    futures::future::ready(Ok(()))
7758                });
7759            })),
7760            ..Default::default()
7761        }))
7762        .await;
7763
7764    let fs = FakeFs::new(cx.executor());
7765    fs.insert_tree(
7766        "/a",
7767        json!({
7768            "main.rs": "fn main() { let a = 5; }",
7769            "other.rs": "// Test file",
7770        }),
7771    )
7772    .await;
7773    let project = Project::test(fs, ["/a".as_ref()], cx).await;
7774    project.update(cx, |project, _| project.languages().add(Arc::new(language)));
7775    let _window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
7776    let _buffer = project
7777        .update(cx, |project, cx| {
7778            project.open_local_buffer("/a/main.rs", cx)
7779        })
7780        .await
7781        .unwrap();
7782    let _fake_server = fake_servers.next().await.unwrap();
7783    update_test_language_settings(cx, |language_settings| {
7784        language_settings.languages.insert(
7785            Arc::clone(&language_name),
7786            LanguageSettingsContent {
7787                tab_size: NonZeroU32::new(8),
7788                ..Default::default()
7789            },
7790        );
7791    });
7792    cx.executor().run_until_parked();
7793    assert_eq!(
7794        server_restarts.load(atomic::Ordering::Acquire),
7795        0,
7796        "Should not restart LSP server on an unrelated change"
7797    );
7798
7799    update_test_project_settings(cx, |project_settings| {
7800        project_settings.lsp.insert(
7801            "Some other server name".into(),
7802            LspSettings {
7803                initialization_options: Some(json!({
7804                    "some other init value": false
7805                })),
7806            },
7807        );
7808    });
7809    cx.executor().run_until_parked();
7810    assert_eq!(
7811        server_restarts.load(atomic::Ordering::Acquire),
7812        0,
7813        "Should not restart LSP server on an unrelated LSP settings change"
7814    );
7815
7816    update_test_project_settings(cx, |project_settings| {
7817        project_settings.lsp.insert(
7818            language_server_name.into(),
7819            LspSettings {
7820                initialization_options: Some(json!({
7821                    "anotherInitValue": false
7822                })),
7823            },
7824        );
7825    });
7826    cx.executor().run_until_parked();
7827    assert_eq!(
7828        server_restarts.load(atomic::Ordering::Acquire),
7829        1,
7830        "Should restart LSP server on a related LSP settings change"
7831    );
7832
7833    update_test_project_settings(cx, |project_settings| {
7834        project_settings.lsp.insert(
7835            language_server_name.into(),
7836            LspSettings {
7837                initialization_options: Some(json!({
7838                    "anotherInitValue": false
7839                })),
7840            },
7841        );
7842    });
7843    cx.executor().run_until_parked();
7844    assert_eq!(
7845        server_restarts.load(atomic::Ordering::Acquire),
7846        1,
7847        "Should not restart LSP server on a related LSP settings change that is the same"
7848    );
7849
7850    update_test_project_settings(cx, |project_settings| {
7851        project_settings.lsp.insert(
7852            language_server_name.into(),
7853            LspSettings {
7854                initialization_options: None,
7855            },
7856        );
7857    });
7858    cx.executor().run_until_parked();
7859    assert_eq!(
7860        server_restarts.load(atomic::Ordering::Acquire),
7861        2,
7862        "Should restart LSP server on another related LSP settings change"
7863    );
7864}
7865
7866#[gpui::test]
7867async fn test_completions_with_additional_edits(cx: &mut gpui::TestAppContext) {
7868    init_test(cx, |_| {});
7869
7870    let mut cx = EditorLspTestContext::new_rust(
7871        lsp::ServerCapabilities {
7872            completion_provider: Some(lsp::CompletionOptions {
7873                trigger_characters: Some(vec![".".to_string()]),
7874                resolve_provider: Some(true),
7875                ..Default::default()
7876            }),
7877            ..Default::default()
7878        },
7879        cx,
7880    )
7881    .await;
7882
7883    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
7884    cx.simulate_keystroke(".");
7885    let completion_item = lsp::CompletionItem {
7886        label: "some".into(),
7887        kind: Some(lsp::CompletionItemKind::SNIPPET),
7888        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
7889        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
7890            kind: lsp::MarkupKind::Markdown,
7891            value: "```rust\nSome(2)\n```".to_string(),
7892        })),
7893        deprecated: Some(false),
7894        sort_text: Some("fffffff2".to_string()),
7895        filter_text: Some("some".to_string()),
7896        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
7897        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
7898            range: lsp::Range {
7899                start: lsp::Position {
7900                    line: 0,
7901                    character: 22,
7902                },
7903                end: lsp::Position {
7904                    line: 0,
7905                    character: 22,
7906                },
7907            },
7908            new_text: "Some(2)".to_string(),
7909        })),
7910        additional_text_edits: Some(vec![lsp::TextEdit {
7911            range: lsp::Range {
7912                start: lsp::Position {
7913                    line: 0,
7914                    character: 20,
7915                },
7916                end: lsp::Position {
7917                    line: 0,
7918                    character: 22,
7919                },
7920            },
7921            new_text: "".to_string(),
7922        }]),
7923        ..Default::default()
7924    };
7925
7926    let closure_completion_item = completion_item.clone();
7927    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
7928        let task_completion_item = closure_completion_item.clone();
7929        async move {
7930            Ok(Some(lsp::CompletionResponse::Array(vec![
7931                task_completion_item,
7932            ])))
7933        }
7934    });
7935
7936    request.next().await;
7937
7938    cx.condition(|editor, _| editor.context_menu_visible())
7939        .await;
7940    let apply_additional_edits = cx.update_editor(|editor, cx| {
7941        editor
7942            .confirm_completion(&ConfirmCompletion::default(), cx)
7943            .unwrap()
7944    });
7945    cx.assert_editor_state(indoc! {"fn main() { let a = 2.Some(2)ˇ; }"});
7946
7947    cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
7948        let task_completion_item = completion_item.clone();
7949        async move { Ok(task_completion_item) }
7950    })
7951    .next()
7952    .await
7953    .unwrap();
7954    apply_additional_edits.await.unwrap();
7955    cx.assert_editor_state(indoc! {"fn main() { let a = Some(2)ˇ; }"});
7956}
7957
7958#[gpui::test]
7959async fn test_completions_in_languages_with_extra_word_characters(cx: &mut gpui::TestAppContext) {
7960    init_test(cx, |_| {});
7961
7962    let mut cx = EditorLspTestContext::new(
7963        Language::new(
7964            LanguageConfig {
7965                path_suffixes: vec!["jsx".into()],
7966                overrides: [(
7967                    "element".into(),
7968                    LanguageConfigOverride {
7969                        word_characters: Override::Set(['-'].into_iter().collect()),
7970                        ..Default::default()
7971                    },
7972                )]
7973                .into_iter()
7974                .collect(),
7975                ..Default::default()
7976            },
7977            Some(tree_sitter_typescript::language_tsx()),
7978        )
7979        .with_override_query("(jsx_self_closing_element) @element")
7980        .unwrap(),
7981        lsp::ServerCapabilities {
7982            completion_provider: Some(lsp::CompletionOptions {
7983                trigger_characters: Some(vec![":".to_string()]),
7984                ..Default::default()
7985            }),
7986            ..Default::default()
7987        },
7988        cx,
7989    )
7990    .await;
7991
7992    cx.lsp
7993        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
7994            Ok(Some(lsp::CompletionResponse::Array(vec![
7995                lsp::CompletionItem {
7996                    label: "bg-blue".into(),
7997                    ..Default::default()
7998                },
7999                lsp::CompletionItem {
8000                    label: "bg-red".into(),
8001                    ..Default::default()
8002                },
8003                lsp::CompletionItem {
8004                    label: "bg-yellow".into(),
8005                    ..Default::default()
8006                },
8007            ])))
8008        });
8009
8010    cx.set_state(r#"<p class="bgˇ" />"#);
8011
8012    // Trigger completion when typing a dash, because the dash is an extra
8013    // word character in the 'element' scope, which contains the cursor.
8014    cx.simulate_keystroke("-");
8015    cx.executor().run_until_parked();
8016    cx.update_editor(|editor, _| {
8017        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
8018            assert_eq!(
8019                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
8020                &["bg-red", "bg-blue", "bg-yellow"]
8021            );
8022        } else {
8023            panic!("expected completion menu to be open");
8024        }
8025    });
8026
8027    cx.simulate_keystroke("l");
8028    cx.executor().run_until_parked();
8029    cx.update_editor(|editor, _| {
8030        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
8031            assert_eq!(
8032                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
8033                &["bg-blue", "bg-yellow"]
8034            );
8035        } else {
8036            panic!("expected completion menu to be open");
8037        }
8038    });
8039
8040    // When filtering completions, consider the character after the '-' to
8041    // be the start of a subword.
8042    cx.set_state(r#"<p class="yelˇ" />"#);
8043    cx.simulate_keystroke("l");
8044    cx.executor().run_until_parked();
8045    cx.update_editor(|editor, _| {
8046        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
8047            assert_eq!(
8048                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
8049                &["bg-yellow"]
8050            );
8051        } else {
8052            panic!("expected completion menu to be open");
8053        }
8054    });
8055}
8056
8057#[gpui::test]
8058async fn test_document_format_with_prettier(cx: &mut gpui::TestAppContext) {
8059    init_test(cx, |settings| {
8060        settings.defaults.formatter = Some(language_settings::Formatter::Prettier)
8061    });
8062
8063    let mut language = Language::new(
8064        LanguageConfig {
8065            name: "Rust".into(),
8066            path_suffixes: vec!["rs".to_string()],
8067            prettier_parser_name: Some("test_parser".to_string()),
8068            ..Default::default()
8069        },
8070        Some(tree_sitter_rust::language()),
8071    );
8072
8073    let test_plugin = "test_plugin";
8074    let _ = language
8075        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
8076            prettier_plugins: vec![test_plugin],
8077            ..Default::default()
8078        }))
8079        .await;
8080
8081    let fs = FakeFs::new(cx.executor());
8082    fs.insert_file("/file.rs", Default::default()).await;
8083
8084    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
8085    let prettier_format_suffix = project::TEST_PRETTIER_FORMAT_SUFFIX;
8086    project.update(cx, |project, _| {
8087        project.languages().add(Arc::new(language));
8088    });
8089    let buffer = project
8090        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
8091        .await
8092        .unwrap();
8093
8094    let buffer_text = "one\ntwo\nthree\n";
8095    let buffer = cx.build_model(|cx| MultiBuffer::singleton(buffer, cx));
8096    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
8097    editor.update(cx, |editor, cx| editor.set_text(buffer_text, cx));
8098
8099    editor
8100        .update(cx, |editor, cx| {
8101            editor.perform_format(project.clone(), FormatTrigger::Manual, cx)
8102        })
8103        .unwrap()
8104        .await;
8105    assert_eq!(
8106        editor.update(cx, |editor, cx| editor.text(cx)),
8107        buffer_text.to_string() + prettier_format_suffix,
8108        "Test prettier formatting was not applied to the original buffer text",
8109    );
8110
8111    update_test_language_settings(cx, |settings| {
8112        settings.defaults.formatter = Some(language_settings::Formatter::Auto)
8113    });
8114    let format = editor.update(cx, |editor, cx| {
8115        editor.perform_format(project.clone(), FormatTrigger::Manual, cx)
8116    });
8117    format.await.unwrap();
8118    assert_eq!(
8119        editor.update(cx, |editor, cx| editor.text(cx)),
8120        buffer_text.to_string() + prettier_format_suffix + "\n" + prettier_format_suffix,
8121        "Autoformatting (via test prettier) was not applied to the original buffer text",
8122    );
8123}
8124
8125fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
8126    let point = DisplayPoint::new(row as u32, column as u32);
8127    point..point
8128}
8129
8130fn assert_selection_ranges(marked_text: &str, view: &mut Editor, cx: &mut ViewContext<Editor>) {
8131    let (text, ranges) = marked_text_ranges(marked_text, true);
8132    assert_eq!(view.text(cx), text);
8133    assert_eq!(
8134        view.selections.ranges(cx),
8135        ranges,
8136        "Assert selections are {}",
8137        marked_text
8138    );
8139}
8140
8141/// Handle completion request passing a marked string specifying where the completion
8142/// should be triggered from using '|' character, what range should be replaced, and what completions
8143/// should be returned using '<' and '>' to delimit the range
8144pub fn handle_completion_request<'a>(
8145    cx: &mut EditorLspTestContext<'a>,
8146    marked_string: &str,
8147    completions: Vec<&'static str>,
8148) -> impl Future<Output = ()> {
8149    let complete_from_marker: TextRangeMarker = '|'.into();
8150    let replace_range_marker: TextRangeMarker = ('<', '>').into();
8151    let (_, mut marked_ranges) = marked_text_ranges_by(
8152        marked_string,
8153        vec![complete_from_marker.clone(), replace_range_marker.clone()],
8154    );
8155
8156    let complete_from_position =
8157        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
8158    let replace_range =
8159        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
8160
8161    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |url, params, _| {
8162        let completions = completions.clone();
8163        async move {
8164            assert_eq!(params.text_document_position.text_document.uri, url.clone());
8165            assert_eq!(
8166                params.text_document_position.position,
8167                complete_from_position
8168            );
8169            Ok(Some(lsp::CompletionResponse::Array(
8170                completions
8171                    .iter()
8172                    .map(|completion_text| lsp::CompletionItem {
8173                        label: completion_text.to_string(),
8174                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
8175                            range: replace_range,
8176                            new_text: completion_text.to_string(),
8177                        })),
8178                        ..Default::default()
8179                    })
8180                    .collect(),
8181            )))
8182        }
8183    });
8184
8185    async move {
8186        request.next().await;
8187    }
8188}
8189
8190fn handle_resolve_completion_request<'a>(
8191    cx: &mut EditorLspTestContext<'a>,
8192    edits: Option<Vec<(&'static str, &'static str)>>,
8193) -> impl Future<Output = ()> {
8194    let edits = edits.map(|edits| {
8195        edits
8196            .iter()
8197            .map(|(marked_string, new_text)| {
8198                let (_, marked_ranges) = marked_text_ranges(marked_string, false);
8199                let replace_range = cx.to_lsp_range(marked_ranges[0].clone());
8200                lsp::TextEdit::new(replace_range, new_text.to_string())
8201            })
8202            .collect::<Vec<_>>()
8203    });
8204
8205    let mut request =
8206        cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
8207            let edits = edits.clone();
8208            async move {
8209                Ok(lsp::CompletionItem {
8210                    additional_text_edits: edits,
8211                    ..Default::default()
8212                })
8213            }
8214        });
8215
8216    async move {
8217        request.next().await;
8218    }
8219}
8220
8221fn handle_copilot_completion_request(
8222    lsp: &lsp::FakeLanguageServer,
8223    completions: Vec<copilot::request::Completion>,
8224    completions_cycling: Vec<copilot::request::Completion>,
8225) {
8226    lsp.handle_request::<copilot::request::GetCompletions, _, _>(move |_params, _cx| {
8227        let completions = completions.clone();
8228        async move {
8229            Ok(copilot::request::GetCompletionsResult {
8230                completions: completions.clone(),
8231            })
8232        }
8233    });
8234    lsp.handle_request::<copilot::request::GetCompletionsCycling, _, _>(move |_params, _cx| {
8235        let completions_cycling = completions_cycling.clone();
8236        async move {
8237            Ok(copilot::request::GetCompletionsResult {
8238                completions: completions_cycling.clone(),
8239            })
8240        }
8241    });
8242}
8243
8244pub(crate) fn update_test_language_settings(
8245    cx: &mut TestAppContext,
8246    f: impl Fn(&mut AllLanguageSettingsContent),
8247) {
8248    cx.update(|cx| {
8249        cx.update_global(|store: &mut SettingsStore, cx| {
8250            store.update_user_settings::<AllLanguageSettings>(cx, f);
8251        });
8252    });
8253}
8254
8255pub(crate) fn update_test_project_settings(
8256    cx: &mut TestAppContext,
8257    f: impl Fn(&mut ProjectSettings),
8258) {
8259    cx.update(|cx| {
8260        cx.update_global(|store: &mut SettingsStore, cx| {
8261            store.update_user_settings::<ProjectSettings>(cx, f);
8262        });
8263    });
8264}
8265
8266pub(crate) fn init_test(cx: &mut TestAppContext, f: fn(&mut AllLanguageSettingsContent)) {
8267    cx.update(|cx| {
8268        let store = SettingsStore::test(cx);
8269        cx.set_global(store);
8270        theme::init(theme::LoadThemes::JustBase, cx);
8271        client::init_settings(cx);
8272        language::init(cx);
8273        Project::init_settings(cx);
8274        workspace::init_settings(cx);
8275        crate::init(cx);
8276    });
8277
8278    update_test_language_settings(cx, f);
8279}