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