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