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_previous(cx: &mut gpui::TestAppContext) {
3825    init_test(cx, |_| {});
3826    {
3827        // `Select previous` without a selection (selects wordwise)
3828        let mut cx = EditorTestContext::new(cx).await;
3829        cx.set_state("abc\nˇabc abc\ndefabc\nabc");
3830
3831        cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
3832            .unwrap();
3833        cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
3834
3835        cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
3836            .unwrap();
3837        cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
3838
3839        cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
3840        cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
3841
3842        cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
3843        cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
3844
3845        cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
3846            .unwrap();
3847        cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\n«abcˇ»");
3848
3849        cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
3850            .unwrap();
3851        cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
3852    }
3853    {
3854        // `Select previous` with a selection
3855        let mut cx = EditorTestContext::new(cx).await;
3856        cx.set_state("abc\n«ˇabc» abc\ndefabc\nabc");
3857
3858        cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
3859            .unwrap();
3860        cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
3861
3862        cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
3863            .unwrap();
3864        cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
3865
3866        cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
3867        cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
3868
3869        cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
3870        cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
3871
3872        cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
3873            .unwrap();
3874        cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndef«abcˇ»\n«abcˇ»");
3875
3876        cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
3877            .unwrap();
3878        cx.assert_editor_state("«abcˇ»\n«ˇabc» «abcˇ»\ndef«abcˇ»\n«abcˇ»");
3879    }
3880}
3881
3882#[gpui::test]
3883async fn test_select_larger_smaller_syntax_node(cx: &mut gpui::TestAppContext) {
3884    init_test(cx, |_| {});
3885
3886    let language = Arc::new(Language::new(
3887        LanguageConfig::default(),
3888        Some(tree_sitter_rust::language()),
3889    ));
3890
3891    let text = r#"
3892        use mod1::mod2::{mod3, mod4};
3893
3894        fn fn_1(param1: bool, param2: &str) {
3895            let var1 = "text";
3896        }
3897    "#
3898    .unindent();
3899
3900    let buffer = cx
3901        .new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), text).with_language(language, cx));
3902    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
3903    let (view, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
3904
3905    view.condition::<crate::EditorEvent>(&cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
3906        .await;
3907
3908    _ = view.update(cx, |view, cx| {
3909        view.change_selections(None, cx, |s| {
3910            s.select_display_ranges([
3911                DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
3912                DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
3913                DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18),
3914            ]);
3915        });
3916        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
3917    });
3918    assert_eq!(
3919        view.update(cx, |view, cx| { view.selections.display_ranges(cx) }),
3920        &[
3921            DisplayPoint::new(0, 23)..DisplayPoint::new(0, 27),
3922            DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
3923            DisplayPoint::new(3, 15)..DisplayPoint::new(3, 21),
3924        ]
3925    );
3926
3927    _ = view.update(cx, |view, cx| {
3928        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
3929    });
3930    assert_eq!(
3931        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
3932        &[
3933            DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
3934            DisplayPoint::new(4, 1)..DisplayPoint::new(2, 0),
3935        ]
3936    );
3937
3938    _ = view.update(cx, |view, cx| {
3939        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
3940    });
3941    assert_eq!(
3942        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
3943        &[DisplayPoint::new(5, 0)..DisplayPoint::new(0, 0)]
3944    );
3945
3946    // Trying to expand the selected syntax node one more time has no effect.
3947    _ = view.update(cx, |view, cx| {
3948        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
3949    });
3950    assert_eq!(
3951        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
3952        &[DisplayPoint::new(5, 0)..DisplayPoint::new(0, 0)]
3953    );
3954
3955    _ = view.update(cx, |view, cx| {
3956        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
3957    });
3958    assert_eq!(
3959        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
3960        &[
3961            DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
3962            DisplayPoint::new(4, 1)..DisplayPoint::new(2, 0),
3963        ]
3964    );
3965
3966    _ = view.update(cx, |view, cx| {
3967        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
3968    });
3969    assert_eq!(
3970        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
3971        &[
3972            DisplayPoint::new(0, 23)..DisplayPoint::new(0, 27),
3973            DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
3974            DisplayPoint::new(3, 15)..DisplayPoint::new(3, 21),
3975        ]
3976    );
3977
3978    _ = view.update(cx, |view, cx| {
3979        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
3980    });
3981    assert_eq!(
3982        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
3983        &[
3984            DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
3985            DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
3986            DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18),
3987        ]
3988    );
3989
3990    // Trying to shrink the selected syntax node one more time has no effect.
3991    _ = view.update(cx, |view, cx| {
3992        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
3993    });
3994    assert_eq!(
3995        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
3996        &[
3997            DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
3998            DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
3999            DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18),
4000        ]
4001    );
4002
4003    // Ensure that we keep expanding the selection if the larger selection starts or ends within
4004    // a fold.
4005    _ = view.update(cx, |view, cx| {
4006        view.fold_ranges(
4007            vec![
4008                Point::new(0, 21)..Point::new(0, 24),
4009                Point::new(3, 20)..Point::new(3, 22),
4010            ],
4011            true,
4012            cx,
4013        );
4014        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
4015    });
4016    assert_eq!(
4017        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
4018        &[
4019            DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
4020            DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
4021            DisplayPoint::new(3, 4)..DisplayPoint::new(3, 23),
4022        ]
4023    );
4024}
4025
4026#[gpui::test]
4027async fn test_autoindent_selections(cx: &mut gpui::TestAppContext) {
4028    init_test(cx, |_| {});
4029
4030    let language = Arc::new(
4031        Language::new(
4032            LanguageConfig {
4033                brackets: BracketPairConfig {
4034                    pairs: vec![
4035                        BracketPair {
4036                            start: "{".to_string(),
4037                            end: "}".to_string(),
4038                            close: false,
4039                            newline: true,
4040                        },
4041                        BracketPair {
4042                            start: "(".to_string(),
4043                            end: ")".to_string(),
4044                            close: false,
4045                            newline: true,
4046                        },
4047                    ],
4048                    ..Default::default()
4049                },
4050                ..Default::default()
4051            },
4052            Some(tree_sitter_rust::language()),
4053        )
4054        .with_indents_query(
4055            r#"
4056                (_ "(" ")" @end) @indent
4057                (_ "{" "}" @end) @indent
4058            "#,
4059        )
4060        .unwrap(),
4061    );
4062
4063    let text = "fn a() {}";
4064
4065    let buffer = cx
4066        .new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), text).with_language(language, cx));
4067    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
4068    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
4069    editor
4070        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
4071        .await;
4072
4073    _ = editor.update(cx, |editor, cx| {
4074        editor.change_selections(None, cx, |s| s.select_ranges([5..5, 8..8, 9..9]));
4075        editor.newline(&Newline, cx);
4076        assert_eq!(editor.text(cx), "fn a(\n    \n) {\n    \n}\n");
4077        assert_eq!(
4078            editor.selections.ranges(cx),
4079            &[
4080                Point::new(1, 4)..Point::new(1, 4),
4081                Point::new(3, 4)..Point::new(3, 4),
4082                Point::new(5, 0)..Point::new(5, 0)
4083            ]
4084        );
4085    });
4086}
4087
4088#[gpui::test]
4089async fn test_autoclose_pairs(cx: &mut gpui::TestAppContext) {
4090    init_test(cx, |_| {});
4091
4092    let mut cx = EditorTestContext::new(cx).await;
4093
4094    let language = Arc::new(Language::new(
4095        LanguageConfig {
4096            brackets: BracketPairConfig {
4097                pairs: vec![
4098                    BracketPair {
4099                        start: "{".to_string(),
4100                        end: "}".to_string(),
4101                        close: true,
4102                        newline: true,
4103                    },
4104                    BracketPair {
4105                        start: "(".to_string(),
4106                        end: ")".to_string(),
4107                        close: true,
4108                        newline: true,
4109                    },
4110                    BracketPair {
4111                        start: "/*".to_string(),
4112                        end: " */".to_string(),
4113                        close: true,
4114                        newline: true,
4115                    },
4116                    BracketPair {
4117                        start: "[".to_string(),
4118                        end: "]".to_string(),
4119                        close: false,
4120                        newline: true,
4121                    },
4122                    BracketPair {
4123                        start: "\"".to_string(),
4124                        end: "\"".to_string(),
4125                        close: true,
4126                        newline: false,
4127                    },
4128                ],
4129                ..Default::default()
4130            },
4131            autoclose_before: "})]".to_string(),
4132            ..Default::default()
4133        },
4134        Some(tree_sitter_rust::language()),
4135    ));
4136
4137    let registry = Arc::new(LanguageRegistry::test());
4138    registry.add(language.clone());
4139    cx.update_buffer(|buffer, cx| {
4140        buffer.set_language_registry(registry);
4141        buffer.set_language(Some(language), cx);
4142    });
4143
4144    cx.set_state(
4145        &r#"
4146            🏀ˇ
4147            εˇ
4148            ❤️ˇ
4149        "#
4150        .unindent(),
4151    );
4152
4153    // autoclose multiple nested brackets at multiple cursors
4154    cx.update_editor(|view, cx| {
4155        view.handle_input("{", cx);
4156        view.handle_input("{", cx);
4157        view.handle_input("{", cx);
4158    });
4159    cx.assert_editor_state(
4160        &"
4161            🏀{{{ˇ}}}
4162            ε{{{ˇ}}}
4163            ❤️{{{ˇ}}}
4164        "
4165        .unindent(),
4166    );
4167
4168    // insert a different closing bracket
4169    cx.update_editor(|view, cx| {
4170        view.handle_input(")", cx);
4171    });
4172    cx.assert_editor_state(
4173        &"
4174            🏀{{{)ˇ}}}
4175            ε{{{)ˇ}}}
4176            ❤️{{{)ˇ}}}
4177        "
4178        .unindent(),
4179    );
4180
4181    // skip over the auto-closed brackets when typing a closing bracket
4182    cx.update_editor(|view, cx| {
4183        view.move_right(&MoveRight, cx);
4184        view.handle_input("}", cx);
4185        view.handle_input("}", cx);
4186        view.handle_input("}", cx);
4187    });
4188    cx.assert_editor_state(
4189        &"
4190            🏀{{{)}}}}ˇ
4191            ε{{{)}}}}ˇ
4192            ❤️{{{)}}}}ˇ
4193        "
4194        .unindent(),
4195    );
4196
4197    // autoclose multi-character pairs
4198    cx.set_state(
4199        &"
4200            ˇ
4201            ˇ
4202        "
4203        .unindent(),
4204    );
4205    cx.update_editor(|view, cx| {
4206        view.handle_input("/", cx);
4207        view.handle_input("*", cx);
4208    });
4209    cx.assert_editor_state(
4210        &"
4211            /*ˇ */
4212            /*ˇ */
4213        "
4214        .unindent(),
4215    );
4216
4217    // one cursor autocloses a multi-character pair, one cursor
4218    // does not autoclose.
4219    cx.set_state(
4220        &"
42214222            ˇ
4223        "
4224        .unindent(),
4225    );
4226    cx.update_editor(|view, cx| view.handle_input("*", cx));
4227    cx.assert_editor_state(
4228        &"
4229            /*ˇ */
42304231        "
4232        .unindent(),
4233    );
4234
4235    // Don't autoclose if the next character isn't whitespace and isn't
4236    // listed in the language's "autoclose_before" section.
4237    cx.set_state("ˇa b");
4238    cx.update_editor(|view, cx| view.handle_input("{", cx));
4239    cx.assert_editor_state("{ˇa b");
4240
4241    // Don't autoclose if `close` is false for the bracket pair
4242    cx.set_state("ˇ");
4243    cx.update_editor(|view, cx| view.handle_input("[", cx));
4244    cx.assert_editor_state("");
4245
4246    // Surround with brackets if text is selected
4247    cx.set_state("«aˇ» b");
4248    cx.update_editor(|view, cx| view.handle_input("{", cx));
4249    cx.assert_editor_state("{«aˇ»} b");
4250
4251    // Autclose pair where the start and end characters are the same
4252    cx.set_state("");
4253    cx.update_editor(|view, cx| view.handle_input("\"", cx));
4254    cx.assert_editor_state("a\"ˇ\"");
4255    cx.update_editor(|view, cx| view.handle_input("\"", cx));
4256    cx.assert_editor_state("a\"\"ˇ");
4257}
4258
4259#[gpui::test]
4260async fn test_autoclose_with_embedded_language(cx: &mut gpui::TestAppContext) {
4261    init_test(cx, |_| {});
4262
4263    let mut cx = EditorTestContext::new(cx).await;
4264
4265    let html_language = Arc::new(
4266        Language::new(
4267            LanguageConfig {
4268                name: "HTML".into(),
4269                brackets: BracketPairConfig {
4270                    pairs: vec![
4271                        BracketPair {
4272                            start: "<".into(),
4273                            end: ">".into(),
4274                            close: true,
4275                            ..Default::default()
4276                        },
4277                        BracketPair {
4278                            start: "{".into(),
4279                            end: "}".into(),
4280                            close: true,
4281                            ..Default::default()
4282                        },
4283                        BracketPair {
4284                            start: "(".into(),
4285                            end: ")".into(),
4286                            close: true,
4287                            ..Default::default()
4288                        },
4289                    ],
4290                    ..Default::default()
4291                },
4292                autoclose_before: "})]>".into(),
4293                ..Default::default()
4294            },
4295            Some(tree_sitter_html::language()),
4296        )
4297        .with_injection_query(
4298            r#"
4299            (script_element
4300                (raw_text) @content
4301                (#set! "language" "javascript"))
4302            "#,
4303        )
4304        .unwrap(),
4305    );
4306
4307    let javascript_language = Arc::new(Language::new(
4308        LanguageConfig {
4309            name: "JavaScript".into(),
4310            brackets: BracketPairConfig {
4311                pairs: vec![
4312                    BracketPair {
4313                        start: "/*".into(),
4314                        end: " */".into(),
4315                        close: true,
4316                        ..Default::default()
4317                    },
4318                    BracketPair {
4319                        start: "{".into(),
4320                        end: "}".into(),
4321                        close: true,
4322                        ..Default::default()
4323                    },
4324                    BracketPair {
4325                        start: "(".into(),
4326                        end: ")".into(),
4327                        close: true,
4328                        ..Default::default()
4329                    },
4330                ],
4331                ..Default::default()
4332            },
4333            autoclose_before: "})]>".into(),
4334            ..Default::default()
4335        },
4336        Some(tree_sitter_typescript::language_tsx()),
4337    ));
4338
4339    let registry = Arc::new(LanguageRegistry::test());
4340    registry.add(html_language.clone());
4341    registry.add(javascript_language.clone());
4342
4343    cx.update_buffer(|buffer, cx| {
4344        buffer.set_language_registry(registry);
4345        buffer.set_language(Some(html_language), cx);
4346    });
4347
4348    cx.set_state(
4349        &r#"
4350            <body>ˇ
4351                <script>
4352                    var x = 1;ˇ
4353                </script>
4354            </body>ˇ
4355        "#
4356        .unindent(),
4357    );
4358
4359    // Precondition: different languages are active at different locations.
4360    cx.update_editor(|editor, cx| {
4361        let snapshot = editor.snapshot(cx);
4362        let cursors = editor.selections.ranges::<usize>(cx);
4363        let languages = cursors
4364            .iter()
4365            .map(|c| snapshot.language_at(c.start).unwrap().name())
4366            .collect::<Vec<_>>();
4367        assert_eq!(
4368            languages,
4369            &["HTML".into(), "JavaScript".into(), "HTML".into()]
4370        );
4371    });
4372
4373    // Angle brackets autoclose in HTML, but not JavaScript.
4374    cx.update_editor(|editor, cx| {
4375        editor.handle_input("<", cx);
4376        editor.handle_input("a", cx);
4377    });
4378    cx.assert_editor_state(
4379        &r#"
4380            <body><aˇ>
4381                <script>
4382                    var x = 1;<aˇ
4383                </script>
4384            </body><aˇ>
4385        "#
4386        .unindent(),
4387    );
4388
4389    // Curly braces and parens autoclose in both HTML and JavaScript.
4390    cx.update_editor(|editor, cx| {
4391        editor.handle_input(" b=", cx);
4392        editor.handle_input("{", cx);
4393        editor.handle_input("c", cx);
4394        editor.handle_input("(", cx);
4395    });
4396    cx.assert_editor_state(
4397        &r#"
4398            <body><a b={c(ˇ)}>
4399                <script>
4400                    var x = 1;<a b={c(ˇ)}
4401                </script>
4402            </body><a b={c(ˇ)}>
4403        "#
4404        .unindent(),
4405    );
4406
4407    // Brackets that were already autoclosed are skipped.
4408    cx.update_editor(|editor, cx| {
4409        editor.handle_input(")", cx);
4410        editor.handle_input("d", cx);
4411        editor.handle_input("}", cx);
4412    });
4413    cx.assert_editor_state(
4414        &r#"
4415            <body><a b={c()d}ˇ>
4416                <script>
4417                    var x = 1;<a b={c()d}ˇ
4418                </script>
4419            </body><a b={c()d}ˇ>
4420        "#
4421        .unindent(),
4422    );
4423    cx.update_editor(|editor, cx| {
4424        editor.handle_input(">", cx);
4425    });
4426    cx.assert_editor_state(
4427        &r#"
4428            <body><a b={c()d}>ˇ
4429                <script>
4430                    var x = 1;<a b={c()d}>ˇ
4431                </script>
4432            </body><a b={c()d}>ˇ
4433        "#
4434        .unindent(),
4435    );
4436
4437    // Reset
4438    cx.set_state(
4439        &r#"
4440            <body>ˇ
4441                <script>
4442                    var x = 1;ˇ
4443                </script>
4444            </body>ˇ
4445        "#
4446        .unindent(),
4447    );
4448
4449    cx.update_editor(|editor, cx| {
4450        editor.handle_input("<", cx);
4451    });
4452    cx.assert_editor_state(
4453        &r#"
4454            <body><ˇ>
4455                <script>
4456                    var x = 1;<ˇ
4457                </script>
4458            </body><ˇ>
4459        "#
4460        .unindent(),
4461    );
4462
4463    // When backspacing, the closing angle brackets are removed.
4464    cx.update_editor(|editor, cx| {
4465        editor.backspace(&Backspace, cx);
4466    });
4467    cx.assert_editor_state(
4468        &r#"
4469            <body>ˇ
4470                <script>
4471                    var x = 1;ˇ
4472                </script>
4473            </body>ˇ
4474        "#
4475        .unindent(),
4476    );
4477
4478    // Block comments autoclose in JavaScript, but not HTML.
4479    cx.update_editor(|editor, cx| {
4480        editor.handle_input("/", cx);
4481        editor.handle_input("*", cx);
4482    });
4483    cx.assert_editor_state(
4484        &r#"
4485            <body>/*ˇ
4486                <script>
4487                    var x = 1;/*ˇ */
4488                </script>
4489            </body>/*ˇ
4490        "#
4491        .unindent(),
4492    );
4493}
4494
4495#[gpui::test]
4496async fn test_autoclose_with_overrides(cx: &mut gpui::TestAppContext) {
4497    init_test(cx, |_| {});
4498
4499    let mut cx = EditorTestContext::new(cx).await;
4500
4501    let rust_language = Arc::new(
4502        Language::new(
4503            LanguageConfig {
4504                name: "Rust".into(),
4505                brackets: serde_json::from_value(json!([
4506                    { "start": "{", "end": "}", "close": true, "newline": true },
4507                    { "start": "\"", "end": "\"", "close": true, "newline": false, "not_in": ["string"] },
4508                ]))
4509                .unwrap(),
4510                autoclose_before: "})]>".into(),
4511                ..Default::default()
4512            },
4513            Some(tree_sitter_rust::language()),
4514        )
4515        .with_override_query("(string_literal) @string")
4516        .unwrap(),
4517    );
4518
4519    let registry = Arc::new(LanguageRegistry::test());
4520    registry.add(rust_language.clone());
4521
4522    cx.update_buffer(|buffer, cx| {
4523        buffer.set_language_registry(registry);
4524        buffer.set_language(Some(rust_language), cx);
4525    });
4526
4527    cx.set_state(
4528        &r#"
4529            let x = ˇ
4530        "#
4531        .unindent(),
4532    );
4533
4534    // Inserting a quotation mark. A closing quotation mark is automatically inserted.
4535    cx.update_editor(|editor, cx| {
4536        editor.handle_input("\"", cx);
4537    });
4538    cx.assert_editor_state(
4539        &r#"
4540            let x = "ˇ"
4541        "#
4542        .unindent(),
4543    );
4544
4545    // Inserting another quotation mark. The cursor moves across the existing
4546    // automatically-inserted quotation mark.
4547    cx.update_editor(|editor, cx| {
4548        editor.handle_input("\"", cx);
4549    });
4550    cx.assert_editor_state(
4551        &r#"
4552            let x = ""ˇ
4553        "#
4554        .unindent(),
4555    );
4556
4557    // Reset
4558    cx.set_state(
4559        &r#"
4560            let x = ˇ
4561        "#
4562        .unindent(),
4563    );
4564
4565    // Inserting a quotation mark inside of a string. A second quotation mark is not inserted.
4566    cx.update_editor(|editor, cx| {
4567        editor.handle_input("\"", cx);
4568        editor.handle_input(" ", cx);
4569        editor.move_left(&Default::default(), cx);
4570        editor.handle_input("\\", cx);
4571        editor.handle_input("\"", cx);
4572    });
4573    cx.assert_editor_state(
4574        &r#"
4575            let x = "\"ˇ "
4576        "#
4577        .unindent(),
4578    );
4579
4580    // Inserting a closing quotation mark at the position of an automatically-inserted quotation
4581    // mark. Nothing is inserted.
4582    cx.update_editor(|editor, cx| {
4583        editor.move_right(&Default::default(), cx);
4584        editor.handle_input("\"", cx);
4585    });
4586    cx.assert_editor_state(
4587        &r#"
4588            let x = "\" "ˇ
4589        "#
4590        .unindent(),
4591    );
4592}
4593
4594#[gpui::test]
4595async fn test_surround_with_pair(cx: &mut gpui::TestAppContext) {
4596    init_test(cx, |_| {});
4597
4598    let language = Arc::new(Language::new(
4599        LanguageConfig {
4600            brackets: BracketPairConfig {
4601                pairs: vec![
4602                    BracketPair {
4603                        start: "{".to_string(),
4604                        end: "}".to_string(),
4605                        close: true,
4606                        newline: true,
4607                    },
4608                    BracketPair {
4609                        start: "/* ".to_string(),
4610                        end: "*/".to_string(),
4611                        close: true,
4612                        ..Default::default()
4613                    },
4614                ],
4615                ..Default::default()
4616            },
4617            ..Default::default()
4618        },
4619        Some(tree_sitter_rust::language()),
4620    ));
4621
4622    let text = r#"
4623        a
4624        b
4625        c
4626    "#
4627    .unindent();
4628
4629    let buffer = cx
4630        .new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), text).with_language(language, cx));
4631    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
4632    let (view, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
4633    view.condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
4634        .await;
4635
4636    _ = view.update(cx, |view, cx| {
4637        view.change_selections(None, cx, |s| {
4638            s.select_display_ranges([
4639                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
4640                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
4641                DisplayPoint::new(2, 0)..DisplayPoint::new(2, 1),
4642            ])
4643        });
4644
4645        view.handle_input("{", cx);
4646        view.handle_input("{", cx);
4647        view.handle_input("{", cx);
4648        assert_eq!(
4649            view.text(cx),
4650            "
4651                {{{a}}}
4652                {{{b}}}
4653                {{{c}}}
4654            "
4655            .unindent()
4656        );
4657        assert_eq!(
4658            view.selections.display_ranges(cx),
4659            [
4660                DisplayPoint::new(0, 3)..DisplayPoint::new(0, 4),
4661                DisplayPoint::new(1, 3)..DisplayPoint::new(1, 4),
4662                DisplayPoint::new(2, 3)..DisplayPoint::new(2, 4)
4663            ]
4664        );
4665
4666        view.undo(&Undo, cx);
4667        view.undo(&Undo, cx);
4668        view.undo(&Undo, cx);
4669        assert_eq!(
4670            view.text(cx),
4671            "
4672                a
4673                b
4674                c
4675            "
4676            .unindent()
4677        );
4678        assert_eq!(
4679            view.selections.display_ranges(cx),
4680            [
4681                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
4682                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
4683                DisplayPoint::new(2, 0)..DisplayPoint::new(2, 1)
4684            ]
4685        );
4686
4687        // Ensure inserting the first character of a multi-byte bracket pair
4688        // doesn't surround the selections with the bracket.
4689        view.handle_input("/", cx);
4690        assert_eq!(
4691            view.text(cx),
4692            "
4693                /
4694                /
4695                /
4696            "
4697            .unindent()
4698        );
4699        assert_eq!(
4700            view.selections.display_ranges(cx),
4701            [
4702                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
4703                DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
4704                DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1)
4705            ]
4706        );
4707
4708        view.undo(&Undo, cx);
4709        assert_eq!(
4710            view.text(cx),
4711            "
4712                a
4713                b
4714                c
4715            "
4716            .unindent()
4717        );
4718        assert_eq!(
4719            view.selections.display_ranges(cx),
4720            [
4721                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
4722                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
4723                DisplayPoint::new(2, 0)..DisplayPoint::new(2, 1)
4724            ]
4725        );
4726
4727        // Ensure inserting the last character of a multi-byte bracket pair
4728        // doesn't surround the selections with the bracket.
4729        view.handle_input("*", cx);
4730        assert_eq!(
4731            view.text(cx),
4732            "
4733                *
4734                *
4735                *
4736            "
4737            .unindent()
4738        );
4739        assert_eq!(
4740            view.selections.display_ranges(cx),
4741            [
4742                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
4743                DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
4744                DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1)
4745            ]
4746        );
4747    });
4748}
4749
4750#[gpui::test]
4751async fn test_delete_autoclose_pair(cx: &mut gpui::TestAppContext) {
4752    init_test(cx, |_| {});
4753
4754    let language = Arc::new(Language::new(
4755        LanguageConfig {
4756            brackets: BracketPairConfig {
4757                pairs: vec![BracketPair {
4758                    start: "{".to_string(),
4759                    end: "}".to_string(),
4760                    close: true,
4761                    newline: true,
4762                }],
4763                ..Default::default()
4764            },
4765            autoclose_before: "}".to_string(),
4766            ..Default::default()
4767        },
4768        Some(tree_sitter_rust::language()),
4769    ));
4770
4771    let text = r#"
4772        a
4773        b
4774        c
4775    "#
4776    .unindent();
4777
4778    let buffer = cx
4779        .new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), text).with_language(language, cx));
4780    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
4781    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
4782    editor
4783        .condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
4784        .await;
4785
4786    _ = editor.update(cx, |editor, cx| {
4787        editor.change_selections(None, cx, |s| {
4788            s.select_ranges([
4789                Point::new(0, 1)..Point::new(0, 1),
4790                Point::new(1, 1)..Point::new(1, 1),
4791                Point::new(2, 1)..Point::new(2, 1),
4792            ])
4793        });
4794
4795        editor.handle_input("{", cx);
4796        editor.handle_input("{", cx);
4797        editor.handle_input("_", cx);
4798        assert_eq!(
4799            editor.text(cx),
4800            "
4801                a{{_}}
4802                b{{_}}
4803                c{{_}}
4804            "
4805            .unindent()
4806        );
4807        assert_eq!(
4808            editor.selections.ranges::<Point>(cx),
4809            [
4810                Point::new(0, 4)..Point::new(0, 4),
4811                Point::new(1, 4)..Point::new(1, 4),
4812                Point::new(2, 4)..Point::new(2, 4)
4813            ]
4814        );
4815
4816        editor.backspace(&Default::default(), cx);
4817        editor.backspace(&Default::default(), cx);
4818        assert_eq!(
4819            editor.text(cx),
4820            "
4821                a{}
4822                b{}
4823                c{}
4824            "
4825            .unindent()
4826        );
4827        assert_eq!(
4828            editor.selections.ranges::<Point>(cx),
4829            [
4830                Point::new(0, 2)..Point::new(0, 2),
4831                Point::new(1, 2)..Point::new(1, 2),
4832                Point::new(2, 2)..Point::new(2, 2)
4833            ]
4834        );
4835
4836        editor.delete_to_previous_word_start(&Default::default(), cx);
4837        assert_eq!(
4838            editor.text(cx),
4839            "
4840                a
4841                b
4842                c
4843            "
4844            .unindent()
4845        );
4846        assert_eq!(
4847            editor.selections.ranges::<Point>(cx),
4848            [
4849                Point::new(0, 1)..Point::new(0, 1),
4850                Point::new(1, 1)..Point::new(1, 1),
4851                Point::new(2, 1)..Point::new(2, 1)
4852            ]
4853        );
4854    });
4855}
4856
4857#[gpui::test]
4858async fn test_snippets(cx: &mut gpui::TestAppContext) {
4859    init_test(cx, |_| {});
4860
4861    let (text, insertion_ranges) = marked_text_ranges(
4862        indoc! {"
4863            a.ˇ b
4864            a.ˇ b
4865            a.ˇ b
4866        "},
4867        false,
4868    );
4869
4870    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
4871    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
4872
4873    _ = editor.update(cx, |editor, cx| {
4874        let snippet = Snippet::parse("f(${1:one}, ${2:two}, ${1:three})$0").unwrap();
4875
4876        editor
4877            .insert_snippet(&insertion_ranges, snippet, cx)
4878            .unwrap();
4879
4880        fn assert(editor: &mut Editor, cx: &mut ViewContext<Editor>, marked_text: &str) {
4881            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
4882            assert_eq!(editor.text(cx), expected_text);
4883            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
4884        }
4885
4886        assert(
4887            editor,
4888            cx,
4889            indoc! {"
4890                a.f(«one», two, «three») b
4891                a.f(«one», two, «three») b
4892                a.f(«one», two, «three») b
4893            "},
4894        );
4895
4896        // Can't move earlier than the first tab stop
4897        assert!(!editor.move_to_prev_snippet_tabstop(cx));
4898        assert(
4899            editor,
4900            cx,
4901            indoc! {"
4902                a.f(«one», two, «three») b
4903                a.f(«one», two, «three») b
4904                a.f(«one», two, «three») b
4905            "},
4906        );
4907
4908        assert!(editor.move_to_next_snippet_tabstop(cx));
4909        assert(
4910            editor,
4911            cx,
4912            indoc! {"
4913                a.f(one, «two», three) b
4914                a.f(one, «two», three) b
4915                a.f(one, «two», three) b
4916            "},
4917        );
4918
4919        editor.move_to_prev_snippet_tabstop(cx);
4920        assert(
4921            editor,
4922            cx,
4923            indoc! {"
4924                a.f(«one», two, «three») b
4925                a.f(«one», two, «three») b
4926                a.f(«one», two, «three») b
4927            "},
4928        );
4929
4930        assert!(editor.move_to_next_snippet_tabstop(cx));
4931        assert(
4932            editor,
4933            cx,
4934            indoc! {"
4935                a.f(one, «two», three) b
4936                a.f(one, «two», three) b
4937                a.f(one, «two», three) b
4938            "},
4939        );
4940        assert!(editor.move_to_next_snippet_tabstop(cx));
4941        assert(
4942            editor,
4943            cx,
4944            indoc! {"
4945                a.f(one, two, three)ˇ b
4946                a.f(one, two, three)ˇ b
4947                a.f(one, two, three)ˇ b
4948            "},
4949        );
4950
4951        // As soon as the last tab stop is reached, snippet state is gone
4952        editor.move_to_prev_snippet_tabstop(cx);
4953        assert(
4954            editor,
4955            cx,
4956            indoc! {"
4957                a.f(one, two, three)ˇ b
4958                a.f(one, two, three)ˇ b
4959                a.f(one, two, three)ˇ b
4960            "},
4961        );
4962    });
4963}
4964
4965#[gpui::test]
4966async fn test_document_format_during_save(cx: &mut gpui::TestAppContext) {
4967    init_test(cx, |_| {});
4968
4969    let mut language = Language::new(
4970        LanguageConfig {
4971            name: "Rust".into(),
4972            path_suffixes: vec!["rs".to_string()],
4973            ..Default::default()
4974        },
4975        Some(tree_sitter_rust::language()),
4976    );
4977    let mut fake_servers = language
4978        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
4979            capabilities: lsp::ServerCapabilities {
4980                document_formatting_provider: Some(lsp::OneOf::Left(true)),
4981                ..Default::default()
4982            },
4983            ..Default::default()
4984        }))
4985        .await;
4986
4987    let fs = FakeFs::new(cx.executor());
4988    fs.insert_file("/file.rs", Default::default()).await;
4989
4990    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
4991    _ = project.update(cx, |project, _| project.languages().add(Arc::new(language)));
4992    let buffer = project
4993        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
4994        .await
4995        .unwrap();
4996
4997    cx.executor().start_waiting();
4998    let fake_server = fake_servers.next().await.unwrap();
4999
5000    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
5001    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
5002    _ = editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
5003    assert!(cx.read(|cx| editor.is_dirty(cx)));
5004
5005    let save = editor
5006        .update(cx, |editor, cx| editor.save(project.clone(), cx))
5007        .unwrap();
5008    fake_server
5009        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
5010            assert_eq!(
5011                params.text_document.uri,
5012                lsp::Url::from_file_path("/file.rs").unwrap()
5013            );
5014            assert_eq!(params.options.tab_size, 4);
5015            Ok(Some(vec![lsp::TextEdit::new(
5016                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
5017                ", ".to_string(),
5018            )]))
5019        })
5020        .next()
5021        .await;
5022    cx.executor().start_waiting();
5023    let _x = save.await;
5024
5025    assert_eq!(
5026        editor.update(cx, |editor, cx| editor.text(cx)),
5027        "one, two\nthree\n"
5028    );
5029    assert!(!cx.read(|cx| editor.is_dirty(cx)));
5030
5031    _ = editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
5032    assert!(cx.read(|cx| editor.is_dirty(cx)));
5033
5034    // Ensure we can still save even if formatting hangs.
5035    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
5036        assert_eq!(
5037            params.text_document.uri,
5038            lsp::Url::from_file_path("/file.rs").unwrap()
5039        );
5040        futures::future::pending::<()>().await;
5041        unreachable!()
5042    });
5043    let save = editor
5044        .update(cx, |editor, cx| editor.save(project.clone(), cx))
5045        .unwrap();
5046    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
5047    cx.executor().start_waiting();
5048    save.await;
5049    assert_eq!(
5050        editor.update(cx, |editor, cx| editor.text(cx)),
5051        "one\ntwo\nthree\n"
5052    );
5053    assert!(!cx.read(|cx| editor.is_dirty(cx)));
5054
5055    // Set rust language override and assert overridden tabsize is sent to language server
5056    update_test_language_settings(cx, |settings| {
5057        settings.languages.insert(
5058            "Rust".into(),
5059            LanguageSettingsContent {
5060                tab_size: NonZeroU32::new(8),
5061                ..Default::default()
5062            },
5063        );
5064    });
5065
5066    let save = editor
5067        .update(cx, |editor, cx| editor.save(project.clone(), cx))
5068        .unwrap();
5069    fake_server
5070        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
5071            assert_eq!(
5072                params.text_document.uri,
5073                lsp::Url::from_file_path("/file.rs").unwrap()
5074            );
5075            assert_eq!(params.options.tab_size, 8);
5076            Ok(Some(vec![]))
5077        })
5078        .next()
5079        .await;
5080    cx.executor().start_waiting();
5081    save.await;
5082}
5083
5084#[gpui::test]
5085async fn test_range_format_during_save(cx: &mut gpui::TestAppContext) {
5086    init_test(cx, |_| {});
5087
5088    let mut language = Language::new(
5089        LanguageConfig {
5090            name: "Rust".into(),
5091            path_suffixes: vec!["rs".to_string()],
5092            ..Default::default()
5093        },
5094        Some(tree_sitter_rust::language()),
5095    );
5096    let mut fake_servers = language
5097        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
5098            capabilities: lsp::ServerCapabilities {
5099                document_range_formatting_provider: Some(lsp::OneOf::Left(true)),
5100                ..Default::default()
5101            },
5102            ..Default::default()
5103        }))
5104        .await;
5105
5106    let fs = FakeFs::new(cx.executor());
5107    fs.insert_file("/file.rs", Default::default()).await;
5108
5109    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
5110    _ = project.update(cx, |project, _| project.languages().add(Arc::new(language)));
5111    let buffer = project
5112        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
5113        .await
5114        .unwrap();
5115
5116    cx.executor().start_waiting();
5117    let fake_server = fake_servers.next().await.unwrap();
5118
5119    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
5120    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
5121    _ = editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
5122    assert!(cx.read(|cx| editor.is_dirty(cx)));
5123
5124    let save = editor
5125        .update(cx, |editor, cx| editor.save(project.clone(), cx))
5126        .unwrap();
5127    fake_server
5128        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
5129            assert_eq!(
5130                params.text_document.uri,
5131                lsp::Url::from_file_path("/file.rs").unwrap()
5132            );
5133            assert_eq!(params.options.tab_size, 4);
5134            Ok(Some(vec![lsp::TextEdit::new(
5135                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
5136                ", ".to_string(),
5137            )]))
5138        })
5139        .next()
5140        .await;
5141    cx.executor().start_waiting();
5142    save.await;
5143    assert_eq!(
5144        editor.update(cx, |editor, cx| editor.text(cx)),
5145        "one, two\nthree\n"
5146    );
5147    assert!(!cx.read(|cx| editor.is_dirty(cx)));
5148
5149    _ = editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
5150    assert!(cx.read(|cx| editor.is_dirty(cx)));
5151
5152    // Ensure we can still save even if formatting hangs.
5153    fake_server.handle_request::<lsp::request::RangeFormatting, _, _>(
5154        move |params, _| async move {
5155            assert_eq!(
5156                params.text_document.uri,
5157                lsp::Url::from_file_path("/file.rs").unwrap()
5158            );
5159            futures::future::pending::<()>().await;
5160            unreachable!()
5161        },
5162    );
5163    let save = editor
5164        .update(cx, |editor, cx| editor.save(project.clone(), cx))
5165        .unwrap();
5166    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
5167    cx.executor().start_waiting();
5168    save.await;
5169    assert_eq!(
5170        editor.update(cx, |editor, cx| editor.text(cx)),
5171        "one\ntwo\nthree\n"
5172    );
5173    assert!(!cx.read(|cx| editor.is_dirty(cx)));
5174
5175    // Set rust language override and assert overridden tabsize is sent to language server
5176    update_test_language_settings(cx, |settings| {
5177        settings.languages.insert(
5178            "Rust".into(),
5179            LanguageSettingsContent {
5180                tab_size: NonZeroU32::new(8),
5181                ..Default::default()
5182            },
5183        );
5184    });
5185
5186    let save = editor
5187        .update(cx, |editor, cx| editor.save(project.clone(), cx))
5188        .unwrap();
5189    fake_server
5190        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
5191            assert_eq!(
5192                params.text_document.uri,
5193                lsp::Url::from_file_path("/file.rs").unwrap()
5194            );
5195            assert_eq!(params.options.tab_size, 8);
5196            Ok(Some(vec![]))
5197        })
5198        .next()
5199        .await;
5200    cx.executor().start_waiting();
5201    save.await;
5202}
5203
5204#[gpui::test]
5205async fn test_document_format_manual_trigger(cx: &mut gpui::TestAppContext) {
5206    init_test(cx, |settings| {
5207        settings.defaults.formatter = Some(language_settings::Formatter::LanguageServer)
5208    });
5209
5210    let mut language = Language::new(
5211        LanguageConfig {
5212            name: "Rust".into(),
5213            path_suffixes: vec!["rs".to_string()],
5214            // Enable Prettier formatting for the same buffer, and ensure
5215            // LSP is called instead of Prettier.
5216            prettier_parser_name: Some("test_parser".to_string()),
5217            ..Default::default()
5218        },
5219        Some(tree_sitter_rust::language()),
5220    );
5221    let mut fake_servers = language
5222        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
5223            capabilities: lsp::ServerCapabilities {
5224                document_formatting_provider: Some(lsp::OneOf::Left(true)),
5225                ..Default::default()
5226            },
5227            ..Default::default()
5228        }))
5229        .await;
5230
5231    let fs = FakeFs::new(cx.executor());
5232    fs.insert_file("/file.rs", Default::default()).await;
5233
5234    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
5235    _ = project.update(cx, |project, _| {
5236        project.languages().add(Arc::new(language));
5237    });
5238    let buffer = project
5239        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
5240        .await
5241        .unwrap();
5242
5243    cx.executor().start_waiting();
5244    let fake_server = fake_servers.next().await.unwrap();
5245
5246    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
5247    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
5248    _ = editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
5249
5250    let format = editor
5251        .update(cx, |editor, cx| {
5252            editor.perform_format(project.clone(), FormatTrigger::Manual, cx)
5253        })
5254        .unwrap();
5255    fake_server
5256        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
5257            assert_eq!(
5258                params.text_document.uri,
5259                lsp::Url::from_file_path("/file.rs").unwrap()
5260            );
5261            assert_eq!(params.options.tab_size, 4);
5262            Ok(Some(vec![lsp::TextEdit::new(
5263                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
5264                ", ".to_string(),
5265            )]))
5266        })
5267        .next()
5268        .await;
5269    cx.executor().start_waiting();
5270    format.await;
5271    assert_eq!(
5272        editor.update(cx, |editor, cx| editor.text(cx)),
5273        "one, two\nthree\n"
5274    );
5275
5276    _ = editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
5277    // Ensure we don't lock if formatting hangs.
5278    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
5279        assert_eq!(
5280            params.text_document.uri,
5281            lsp::Url::from_file_path("/file.rs").unwrap()
5282        );
5283        futures::future::pending::<()>().await;
5284        unreachable!()
5285    });
5286    let format = editor
5287        .update(cx, |editor, cx| {
5288            editor.perform_format(project, FormatTrigger::Manual, cx)
5289        })
5290        .unwrap();
5291    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
5292    cx.executor().start_waiting();
5293    format.await;
5294    assert_eq!(
5295        editor.update(cx, |editor, cx| editor.text(cx)),
5296        "one\ntwo\nthree\n"
5297    );
5298}
5299
5300#[gpui::test]
5301async fn test_concurrent_format_requests(cx: &mut gpui::TestAppContext) {
5302    init_test(cx, |_| {});
5303
5304    let mut cx = EditorLspTestContext::new_rust(
5305        lsp::ServerCapabilities {
5306            document_formatting_provider: Some(lsp::OneOf::Left(true)),
5307            ..Default::default()
5308        },
5309        cx,
5310    )
5311    .await;
5312
5313    cx.set_state(indoc! {"
5314        one.twoˇ
5315    "});
5316
5317    // The format request takes a long time. When it completes, it inserts
5318    // a newline and an indent before the `.`
5319    cx.lsp
5320        .handle_request::<lsp::request::Formatting, _, _>(move |_, cx| {
5321            let executor = cx.background_executor().clone();
5322            async move {
5323                executor.timer(Duration::from_millis(100)).await;
5324                Ok(Some(vec![lsp::TextEdit {
5325                    range: lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 3)),
5326                    new_text: "\n    ".into(),
5327                }]))
5328            }
5329        });
5330
5331    // Submit a format request.
5332    let format_1 = cx
5333        .update_editor(|editor, cx| editor.format(&Format, cx))
5334        .unwrap();
5335    cx.executor().run_until_parked();
5336
5337    // Submit a second format request.
5338    let format_2 = cx
5339        .update_editor(|editor, cx| editor.format(&Format, cx))
5340        .unwrap();
5341    cx.executor().run_until_parked();
5342
5343    // Wait for both format requests to complete
5344    cx.executor().advance_clock(Duration::from_millis(200));
5345    cx.executor().start_waiting();
5346    format_1.await.unwrap();
5347    cx.executor().start_waiting();
5348    format_2.await.unwrap();
5349
5350    // The formatting edits only happens once.
5351    cx.assert_editor_state(indoc! {"
5352        one
5353            .twoˇ
5354    "});
5355}
5356
5357#[gpui::test]
5358async fn test_strip_whitespace_and_format_via_lsp(cx: &mut gpui::TestAppContext) {
5359    init_test(cx, |settings| {
5360        settings.defaults.formatter = Some(language_settings::Formatter::Auto)
5361    });
5362
5363    let mut cx = EditorLspTestContext::new_rust(
5364        lsp::ServerCapabilities {
5365            document_formatting_provider: Some(lsp::OneOf::Left(true)),
5366            ..Default::default()
5367        },
5368        cx,
5369    )
5370    .await;
5371
5372    // Set up a buffer white some trailing whitespace and no trailing newline.
5373    cx.set_state(
5374        &[
5375            "one ",   //
5376            "twoˇ",   //
5377            "three ", //
5378            "four",   //
5379        ]
5380        .join("\n"),
5381    );
5382
5383    // Submit a format request.
5384    let format = cx
5385        .update_editor(|editor, cx| editor.format(&Format, cx))
5386        .unwrap();
5387
5388    // Record which buffer changes have been sent to the language server
5389    let buffer_changes = Arc::new(Mutex::new(Vec::new()));
5390    cx.lsp
5391        .handle_notification::<lsp::notification::DidChangeTextDocument, _>({
5392            let buffer_changes = buffer_changes.clone();
5393            move |params, _| {
5394                buffer_changes.lock().extend(
5395                    params
5396                        .content_changes
5397                        .into_iter()
5398                        .map(|e| (e.range.unwrap(), e.text)),
5399                );
5400            }
5401        });
5402
5403    // Handle formatting requests to the language server.
5404    cx.lsp.handle_request::<lsp::request::Formatting, _, _>({
5405        let buffer_changes = buffer_changes.clone();
5406        move |_, _| {
5407            // When formatting is requested, trailing whitespace has already been stripped,
5408            // and the trailing newline has already been added.
5409            assert_eq!(
5410                &buffer_changes.lock()[1..],
5411                &[
5412                    (
5413                        lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 4)),
5414                        "".into()
5415                    ),
5416                    (
5417                        lsp::Range::new(lsp::Position::new(2, 5), lsp::Position::new(2, 6)),
5418                        "".into()
5419                    ),
5420                    (
5421                        lsp::Range::new(lsp::Position::new(3, 4), lsp::Position::new(3, 4)),
5422                        "\n".into()
5423                    ),
5424                ]
5425            );
5426
5427            // Insert blank lines between each line of the buffer.
5428            async move {
5429                Ok(Some(vec![
5430                    lsp::TextEdit {
5431                        range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 0)),
5432                        new_text: "\n".into(),
5433                    },
5434                    lsp::TextEdit {
5435                        range: lsp::Range::new(lsp::Position::new(2, 0), lsp::Position::new(2, 0)),
5436                        new_text: "\n".into(),
5437                    },
5438                ]))
5439            }
5440        }
5441    });
5442
5443    // After formatting the buffer, the trailing whitespace is stripped,
5444    // a newline is appended, and the edits provided by the language server
5445    // have been applied.
5446    format.await.unwrap();
5447    cx.assert_editor_state(
5448        &[
5449            "one",   //
5450            "",      //
5451            "twoˇ",  //
5452            "",      //
5453            "three", //
5454            "four",  //
5455            "",      //
5456        ]
5457        .join("\n"),
5458    );
5459
5460    // Undoing the formatting undoes the trailing whitespace removal, the
5461    // trailing newline, and the LSP edits.
5462    cx.update_buffer(|buffer, cx| buffer.undo(cx));
5463    cx.assert_editor_state(
5464        &[
5465            "one ",   //
5466            "twoˇ",   //
5467            "three ", //
5468            "four",   //
5469        ]
5470        .join("\n"),
5471    );
5472}
5473
5474#[gpui::test]
5475async fn test_completion(cx: &mut gpui::TestAppContext) {
5476    init_test(cx, |_| {});
5477
5478    let mut cx = EditorLspTestContext::new_rust(
5479        lsp::ServerCapabilities {
5480            completion_provider: Some(lsp::CompletionOptions {
5481                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
5482                resolve_provider: Some(true),
5483                ..Default::default()
5484            }),
5485            ..Default::default()
5486        },
5487        cx,
5488    )
5489    .await;
5490
5491    cx.set_state(indoc! {"
5492        oneˇ
5493        two
5494        three
5495    "});
5496    cx.simulate_keystroke(".");
5497    handle_completion_request(
5498        &mut cx,
5499        indoc! {"
5500            one.|<>
5501            two
5502            three
5503        "},
5504        vec!["first_completion", "second_completion"],
5505    )
5506    .await;
5507    cx.condition(|editor, _| editor.context_menu_visible())
5508        .await;
5509    let apply_additional_edits = cx.update_editor(|editor, cx| {
5510        editor.context_menu_next(&Default::default(), cx);
5511        editor
5512            .confirm_completion(&ConfirmCompletion::default(), cx)
5513            .unwrap()
5514    });
5515    cx.assert_editor_state(indoc! {"
5516        one.second_completionˇ
5517        two
5518        three
5519    "});
5520
5521    handle_resolve_completion_request(
5522        &mut cx,
5523        Some(vec![
5524            (
5525                //This overlaps with the primary completion edit which is
5526                //misbehavior from the LSP spec, test that we filter it out
5527                indoc! {"
5528                    one.second_ˇcompletion
5529                    two
5530                    threeˇ
5531                "},
5532                "overlapping additional edit",
5533            ),
5534            (
5535                indoc! {"
5536                    one.second_completion
5537                    two
5538                    threeˇ
5539                "},
5540                "\nadditional edit",
5541            ),
5542        ]),
5543    )
5544    .await;
5545    apply_additional_edits.await.unwrap();
5546    cx.assert_editor_state(indoc! {"
5547        one.second_completionˇ
5548        two
5549        three
5550        additional edit
5551    "});
5552
5553    cx.set_state(indoc! {"
5554        one.second_completion
5555        twoˇ
5556        threeˇ
5557        additional edit
5558    "});
5559    cx.simulate_keystroke(" ");
5560    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
5561    cx.simulate_keystroke("s");
5562    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
5563
5564    cx.assert_editor_state(indoc! {"
5565        one.second_completion
5566        two sˇ
5567        three sˇ
5568        additional edit
5569    "});
5570    handle_completion_request(
5571        &mut cx,
5572        indoc! {"
5573            one.second_completion
5574            two s
5575            three <s|>
5576            additional edit
5577        "},
5578        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
5579    )
5580    .await;
5581    cx.condition(|editor, _| editor.context_menu_visible())
5582        .await;
5583
5584    cx.simulate_keystroke("i");
5585
5586    handle_completion_request(
5587        &mut cx,
5588        indoc! {"
5589            one.second_completion
5590            two si
5591            three <si|>
5592            additional edit
5593        "},
5594        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
5595    )
5596    .await;
5597    cx.condition(|editor, _| editor.context_menu_visible())
5598        .await;
5599
5600    let apply_additional_edits = cx.update_editor(|editor, cx| {
5601        editor
5602            .confirm_completion(&ConfirmCompletion::default(), cx)
5603            .unwrap()
5604    });
5605    cx.assert_editor_state(indoc! {"
5606        one.second_completion
5607        two sixth_completionˇ
5608        three sixth_completionˇ
5609        additional edit
5610    "});
5611
5612    handle_resolve_completion_request(&mut cx, None).await;
5613    apply_additional_edits.await.unwrap();
5614
5615    _ = cx.update(|cx| {
5616        cx.update_global::<SettingsStore, _>(|settings, cx| {
5617            settings.update_user_settings::<EditorSettings>(cx, |settings| {
5618                settings.show_completions_on_input = Some(false);
5619            });
5620        })
5621    });
5622    cx.set_state("editorˇ");
5623    cx.simulate_keystroke(".");
5624    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
5625    cx.simulate_keystroke("c");
5626    cx.simulate_keystroke("l");
5627    cx.simulate_keystroke("o");
5628    cx.assert_editor_state("editor.cloˇ");
5629    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
5630    cx.update_editor(|editor, cx| {
5631        editor.show_completions(&ShowCompletions, cx);
5632    });
5633    handle_completion_request(&mut cx, "editor.<clo|>", vec!["close", "clobber"]).await;
5634    cx.condition(|editor, _| editor.context_menu_visible())
5635        .await;
5636    let apply_additional_edits = cx.update_editor(|editor, cx| {
5637        editor
5638            .confirm_completion(&ConfirmCompletion::default(), cx)
5639            .unwrap()
5640    });
5641    cx.assert_editor_state("editor.closeˇ");
5642    handle_resolve_completion_request(&mut cx, None).await;
5643    apply_additional_edits.await.unwrap();
5644}
5645
5646#[gpui::test]
5647async fn test_toggle_comment(cx: &mut gpui::TestAppContext) {
5648    init_test(cx, |_| {});
5649    let mut cx = EditorTestContext::new(cx).await;
5650    let language = Arc::new(Language::new(
5651        LanguageConfig {
5652            line_comment: Some("// ".into()),
5653            ..Default::default()
5654        },
5655        Some(tree_sitter_rust::language()),
5656    ));
5657    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
5658
5659    // If multiple selections intersect a line, the line is only toggled once.
5660    cx.set_state(indoc! {"
5661        fn a() {
5662            «//b();
5663            ˇ»// «c();
5664            //ˇ»  d();
5665        }
5666    "});
5667
5668    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
5669
5670    cx.assert_editor_state(indoc! {"
5671        fn a() {
5672            «b();
5673            c();
5674            ˇ» d();
5675        }
5676    "});
5677
5678    // The comment prefix is inserted at the same column for every line in a
5679    // selection.
5680    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
5681
5682    cx.assert_editor_state(indoc! {"
5683        fn a() {
5684            // «b();
5685            // c();
5686            ˇ»//  d();
5687        }
5688    "});
5689
5690    // If a selection ends at the beginning of a line, that line is not toggled.
5691    cx.set_selections_state(indoc! {"
5692        fn a() {
5693            // b();
5694            «// c();
5695        ˇ»    //  d();
5696        }
5697    "});
5698
5699    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
5700
5701    cx.assert_editor_state(indoc! {"
5702        fn a() {
5703            // b();
5704            «c();
5705        ˇ»    //  d();
5706        }
5707    "});
5708
5709    // If a selection span a single line and is empty, the line is toggled.
5710    cx.set_state(indoc! {"
5711        fn a() {
5712            a();
5713            b();
5714        ˇ
5715        }
5716    "});
5717
5718    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
5719
5720    cx.assert_editor_state(indoc! {"
5721        fn a() {
5722            a();
5723            b();
5724        //•ˇ
5725        }
5726    "});
5727
5728    // If a selection span multiple lines, empty lines are not toggled.
5729    cx.set_state(indoc! {"
5730        fn a() {
5731            «a();
5732
5733            c();ˇ»
5734        }
5735    "});
5736
5737    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
5738
5739    cx.assert_editor_state(indoc! {"
5740        fn a() {
5741            // «a();
5742
5743            // c();ˇ»
5744        }
5745    "});
5746}
5747
5748#[gpui::test]
5749async fn test_advance_downward_on_toggle_comment(cx: &mut gpui::TestAppContext) {
5750    init_test(cx, |_| {});
5751
5752    let language = Arc::new(Language::new(
5753        LanguageConfig {
5754            line_comment: Some("// ".into()),
5755            ..Default::default()
5756        },
5757        Some(tree_sitter_rust::language()),
5758    ));
5759
5760    let registry = Arc::new(LanguageRegistry::test());
5761    registry.add(language.clone());
5762
5763    let mut cx = EditorTestContext::new(cx).await;
5764    cx.update_buffer(|buffer, cx| {
5765        buffer.set_language_registry(registry);
5766        buffer.set_language(Some(language), cx);
5767    });
5768
5769    let toggle_comments = &ToggleComments {
5770        advance_downwards: true,
5771    };
5772
5773    // Single cursor on one line -> advance
5774    // Cursor moves horizontally 3 characters as well on non-blank line
5775    cx.set_state(indoc!(
5776        "fn a() {
5777             ˇdog();
5778             cat();
5779        }"
5780    ));
5781    cx.update_editor(|editor, cx| {
5782        editor.toggle_comments(toggle_comments, cx);
5783    });
5784    cx.assert_editor_state(indoc!(
5785        "fn a() {
5786             // dog();
5787             catˇ();
5788        }"
5789    ));
5790
5791    // Single selection on one line -> don't advance
5792    cx.set_state(indoc!(
5793        "fn a() {
5794             «dog()ˇ»;
5795             cat();
5796        }"
5797    ));
5798    cx.update_editor(|editor, cx| {
5799        editor.toggle_comments(toggle_comments, cx);
5800    });
5801    cx.assert_editor_state(indoc!(
5802        "fn a() {
5803             // «dog()ˇ»;
5804             cat();
5805        }"
5806    ));
5807
5808    // Multiple cursors on one line -> advance
5809    cx.set_state(indoc!(
5810        "fn a() {
5811             ˇdˇog();
5812             cat();
5813        }"
5814    ));
5815    cx.update_editor(|editor, cx| {
5816        editor.toggle_comments(toggle_comments, cx);
5817    });
5818    cx.assert_editor_state(indoc!(
5819        "fn a() {
5820             // dog();
5821             catˇ(ˇ);
5822        }"
5823    ));
5824
5825    // Multiple cursors on one line, with selection -> don't advance
5826    cx.set_state(indoc!(
5827        "fn a() {
5828             ˇdˇog«()ˇ»;
5829             cat();
5830        }"
5831    ));
5832    cx.update_editor(|editor, cx| {
5833        editor.toggle_comments(toggle_comments, cx);
5834    });
5835    cx.assert_editor_state(indoc!(
5836        "fn a() {
5837             // ˇdˇog«()ˇ»;
5838             cat();
5839        }"
5840    ));
5841
5842    // Single cursor on one line -> advance
5843    // Cursor moves to column 0 on blank line
5844    cx.set_state(indoc!(
5845        "fn a() {
5846             ˇdog();
5847
5848             cat();
5849        }"
5850    ));
5851    cx.update_editor(|editor, cx| {
5852        editor.toggle_comments(toggle_comments, cx);
5853    });
5854    cx.assert_editor_state(indoc!(
5855        "fn a() {
5856             // dog();
5857        ˇ
5858             cat();
5859        }"
5860    ));
5861
5862    // Single cursor on one line -> advance
5863    // Cursor starts and ends at column 0
5864    cx.set_state(indoc!(
5865        "fn a() {
5866         ˇ    dog();
5867             cat();
5868        }"
5869    ));
5870    cx.update_editor(|editor, cx| {
5871        editor.toggle_comments(toggle_comments, cx);
5872    });
5873    cx.assert_editor_state(indoc!(
5874        "fn a() {
5875             // dog();
5876         ˇ    cat();
5877        }"
5878    ));
5879}
5880
5881#[gpui::test]
5882async fn test_toggle_block_comment(cx: &mut gpui::TestAppContext) {
5883    init_test(cx, |_| {});
5884
5885    let mut cx = EditorTestContext::new(cx).await;
5886
5887    let html_language = Arc::new(
5888        Language::new(
5889            LanguageConfig {
5890                name: "HTML".into(),
5891                block_comment: Some(("<!-- ".into(), " -->".into())),
5892                ..Default::default()
5893            },
5894            Some(tree_sitter_html::language()),
5895        )
5896        .with_injection_query(
5897            r#"
5898            (script_element
5899                (raw_text) @content
5900                (#set! "language" "javascript"))
5901            "#,
5902        )
5903        .unwrap(),
5904    );
5905
5906    let javascript_language = Arc::new(Language::new(
5907        LanguageConfig {
5908            name: "JavaScript".into(),
5909            line_comment: Some("// ".into()),
5910            ..Default::default()
5911        },
5912        Some(tree_sitter_typescript::language_tsx()),
5913    ));
5914
5915    let registry = Arc::new(LanguageRegistry::test());
5916    registry.add(html_language.clone());
5917    registry.add(javascript_language.clone());
5918
5919    cx.update_buffer(|buffer, cx| {
5920        buffer.set_language_registry(registry);
5921        buffer.set_language(Some(html_language), cx);
5922    });
5923
5924    // Toggle comments for empty selections
5925    cx.set_state(
5926        &r#"
5927            <p>A</p>ˇ
5928            <p>B</p>ˇ
5929            <p>C</p>ˇ
5930        "#
5931        .unindent(),
5932    );
5933    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
5934    cx.assert_editor_state(
5935        &r#"
5936            <!-- <p>A</p>ˇ -->
5937            <!-- <p>B</p>ˇ -->
5938            <!-- <p>C</p>ˇ -->
5939        "#
5940        .unindent(),
5941    );
5942    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
5943    cx.assert_editor_state(
5944        &r#"
5945            <p>A</p>ˇ
5946            <p>B</p>ˇ
5947            <p>C</p>ˇ
5948        "#
5949        .unindent(),
5950    );
5951
5952    // Toggle comments for mixture of empty and non-empty selections, where
5953    // multiple selections occupy a given line.
5954    cx.set_state(
5955        &r#"
5956            <p>A«</p>
5957            <p>ˇ»B</p>ˇ
5958            <p>C«</p>
5959            <p>ˇ»D</p>ˇ
5960        "#
5961        .unindent(),
5962    );
5963
5964    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
5965    cx.assert_editor_state(
5966        &r#"
5967            <!-- <p>A«</p>
5968            <p>ˇ»B</p>ˇ -->
5969            <!-- <p>C«</p>
5970            <p>ˇ»D</p>ˇ -->
5971        "#
5972        .unindent(),
5973    );
5974    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
5975    cx.assert_editor_state(
5976        &r#"
5977            <p>A«</p>
5978            <p>ˇ»B</p>ˇ
5979            <p>C«</p>
5980            <p>ˇ»D</p>ˇ
5981        "#
5982        .unindent(),
5983    );
5984
5985    // Toggle comments when different languages are active for different
5986    // selections.
5987    cx.set_state(
5988        &r#"
5989            ˇ<script>
5990                ˇvar x = new Y();
5991            ˇ</script>
5992        "#
5993        .unindent(),
5994    );
5995    cx.executor().run_until_parked();
5996    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
5997    cx.assert_editor_state(
5998        &r#"
5999            <!-- ˇ<script> -->
6000                // ˇvar x = new Y();
6001            <!-- ˇ</script> -->
6002        "#
6003        .unindent(),
6004    );
6005}
6006
6007#[gpui::test]
6008fn test_editing_disjoint_excerpts(cx: &mut TestAppContext) {
6009    init_test(cx, |_| {});
6010
6011    let buffer = cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), sample_text(3, 4, 'a')));
6012    let multibuffer = cx.new_model(|cx| {
6013        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
6014        multibuffer.push_excerpts(
6015            buffer.clone(),
6016            [
6017                ExcerptRange {
6018                    context: Point::new(0, 0)..Point::new(0, 4),
6019                    primary: None,
6020                },
6021                ExcerptRange {
6022                    context: Point::new(1, 0)..Point::new(1, 4),
6023                    primary: None,
6024                },
6025            ],
6026            cx,
6027        );
6028        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb");
6029        multibuffer
6030    });
6031
6032    let (view, cx) = cx.add_window_view(|cx| build_editor(multibuffer, cx));
6033    _ = view.update(cx, |view, cx| {
6034        assert_eq!(view.text(cx), "aaaa\nbbbb");
6035        view.change_selections(None, cx, |s| {
6036            s.select_ranges([
6037                Point::new(0, 0)..Point::new(0, 0),
6038                Point::new(1, 0)..Point::new(1, 0),
6039            ])
6040        });
6041
6042        view.handle_input("X", cx);
6043        assert_eq!(view.text(cx), "Xaaaa\nXbbbb");
6044        assert_eq!(
6045            view.selections.ranges(cx),
6046            [
6047                Point::new(0, 1)..Point::new(0, 1),
6048                Point::new(1, 1)..Point::new(1, 1),
6049            ]
6050        );
6051
6052        // Ensure the cursor's head is respected when deleting across an excerpt boundary.
6053        view.change_selections(None, cx, |s| {
6054            s.select_ranges([Point::new(0, 2)..Point::new(1, 2)])
6055        });
6056        view.backspace(&Default::default(), cx);
6057        assert_eq!(view.text(cx), "Xa\nbbb");
6058        assert_eq!(
6059            view.selections.ranges(cx),
6060            [Point::new(1, 0)..Point::new(1, 0)]
6061        );
6062
6063        view.change_selections(None, cx, |s| {
6064            s.select_ranges([Point::new(1, 1)..Point::new(0, 1)])
6065        });
6066        view.backspace(&Default::default(), cx);
6067        assert_eq!(view.text(cx), "X\nbb");
6068        assert_eq!(
6069            view.selections.ranges(cx),
6070            [Point::new(0, 1)..Point::new(0, 1)]
6071        );
6072    });
6073}
6074
6075#[gpui::test]
6076fn test_editing_overlapping_excerpts(cx: &mut TestAppContext) {
6077    init_test(cx, |_| {});
6078
6079    let markers = vec![('[', ']').into(), ('(', ')').into()];
6080    let (initial_text, mut excerpt_ranges) = marked_text_ranges_by(
6081        indoc! {"
6082            [aaaa
6083            (bbbb]
6084            cccc)",
6085        },
6086        markers.clone(),
6087    );
6088    let excerpt_ranges = markers.into_iter().map(|marker| {
6089        let context = excerpt_ranges.remove(&marker).unwrap()[0].clone();
6090        ExcerptRange {
6091            context,
6092            primary: None,
6093        }
6094    });
6095    let buffer = cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), initial_text));
6096    let multibuffer = cx.new_model(|cx| {
6097        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
6098        multibuffer.push_excerpts(buffer, excerpt_ranges, cx);
6099        multibuffer
6100    });
6101
6102    let (view, cx) = cx.add_window_view(|cx| build_editor(multibuffer, cx));
6103    _ = view.update(cx, |view, cx| {
6104        let (expected_text, selection_ranges) = marked_text_ranges(
6105            indoc! {"
6106                aaaa
6107                bˇbbb
6108                bˇbbˇb
6109                cccc"
6110            },
6111            true,
6112        );
6113        assert_eq!(view.text(cx), expected_text);
6114        view.change_selections(None, cx, |s| s.select_ranges(selection_ranges));
6115
6116        view.handle_input("X", cx);
6117
6118        let (expected_text, expected_selections) = marked_text_ranges(
6119            indoc! {"
6120                aaaa
6121                bXˇbbXb
6122                bXˇbbXˇb
6123                cccc"
6124            },
6125            false,
6126        );
6127        assert_eq!(view.text(cx), expected_text);
6128        assert_eq!(view.selections.ranges(cx), expected_selections);
6129
6130        view.newline(&Newline, cx);
6131        let (expected_text, expected_selections) = marked_text_ranges(
6132            indoc! {"
6133                aaaa
6134                bX
6135                ˇbbX
6136                b
6137                bX
6138                ˇbbX
6139                ˇb
6140                cccc"
6141            },
6142            false,
6143        );
6144        assert_eq!(view.text(cx), expected_text);
6145        assert_eq!(view.selections.ranges(cx), expected_selections);
6146    });
6147}
6148
6149#[gpui::test]
6150fn test_refresh_selections(cx: &mut TestAppContext) {
6151    init_test(cx, |_| {});
6152
6153    let buffer = cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), sample_text(3, 4, 'a')));
6154    let mut excerpt1_id = None;
6155    let multibuffer = cx.new_model(|cx| {
6156        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
6157        excerpt1_id = multibuffer
6158            .push_excerpts(
6159                buffer.clone(),
6160                [
6161                    ExcerptRange {
6162                        context: Point::new(0, 0)..Point::new(1, 4),
6163                        primary: None,
6164                    },
6165                    ExcerptRange {
6166                        context: Point::new(1, 0)..Point::new(2, 4),
6167                        primary: None,
6168                    },
6169                ],
6170                cx,
6171            )
6172            .into_iter()
6173            .next();
6174        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
6175        multibuffer
6176    });
6177
6178    let editor = cx.add_window(|cx| {
6179        let mut editor = build_editor(multibuffer.clone(), cx);
6180        let snapshot = editor.snapshot(cx);
6181        editor.change_selections(None, cx, |s| {
6182            s.select_ranges([Point::new(1, 3)..Point::new(1, 3)])
6183        });
6184        editor.begin_selection(Point::new(2, 1).to_display_point(&snapshot), true, 1, cx);
6185        assert_eq!(
6186            editor.selections.ranges(cx),
6187            [
6188                Point::new(1, 3)..Point::new(1, 3),
6189                Point::new(2, 1)..Point::new(2, 1),
6190            ]
6191        );
6192        editor
6193    });
6194
6195    // Refreshing selections is a no-op when excerpts haven't changed.
6196    _ = editor.update(cx, |editor, cx| {
6197        editor.change_selections(None, cx, |s| s.refresh());
6198        assert_eq!(
6199            editor.selections.ranges(cx),
6200            [
6201                Point::new(1, 3)..Point::new(1, 3),
6202                Point::new(2, 1)..Point::new(2, 1),
6203            ]
6204        );
6205    });
6206
6207    _ = multibuffer.update(cx, |multibuffer, cx| {
6208        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
6209    });
6210    _ = editor.update(cx, |editor, cx| {
6211        // Removing an excerpt causes the first selection to become degenerate.
6212        assert_eq!(
6213            editor.selections.ranges(cx),
6214            [
6215                Point::new(0, 0)..Point::new(0, 0),
6216                Point::new(0, 1)..Point::new(0, 1)
6217            ]
6218        );
6219
6220        // Refreshing selections will relocate the first selection to the original buffer
6221        // location.
6222        editor.change_selections(None, cx, |s| s.refresh());
6223        assert_eq!(
6224            editor.selections.ranges(cx),
6225            [
6226                Point::new(0, 1)..Point::new(0, 1),
6227                Point::new(0, 3)..Point::new(0, 3)
6228            ]
6229        );
6230        assert!(editor.selections.pending_anchor().is_some());
6231    });
6232}
6233
6234#[gpui::test]
6235fn test_refresh_selections_while_selecting_with_mouse(cx: &mut TestAppContext) {
6236    init_test(cx, |_| {});
6237
6238    let buffer = cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), sample_text(3, 4, 'a')));
6239    let mut excerpt1_id = None;
6240    let multibuffer = cx.new_model(|cx| {
6241        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
6242        excerpt1_id = multibuffer
6243            .push_excerpts(
6244                buffer.clone(),
6245                [
6246                    ExcerptRange {
6247                        context: Point::new(0, 0)..Point::new(1, 4),
6248                        primary: None,
6249                    },
6250                    ExcerptRange {
6251                        context: Point::new(1, 0)..Point::new(2, 4),
6252                        primary: None,
6253                    },
6254                ],
6255                cx,
6256            )
6257            .into_iter()
6258            .next();
6259        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
6260        multibuffer
6261    });
6262
6263    let editor = cx.add_window(|cx| {
6264        let mut editor = build_editor(multibuffer.clone(), cx);
6265        let snapshot = editor.snapshot(cx);
6266        editor.begin_selection(Point::new(1, 3).to_display_point(&snapshot), false, 1, cx);
6267        assert_eq!(
6268            editor.selections.ranges(cx),
6269            [Point::new(1, 3)..Point::new(1, 3)]
6270        );
6271        editor
6272    });
6273
6274    _ = multibuffer.update(cx, |multibuffer, cx| {
6275        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
6276    });
6277    _ = editor.update(cx, |editor, cx| {
6278        assert_eq!(
6279            editor.selections.ranges(cx),
6280            [Point::new(0, 0)..Point::new(0, 0)]
6281        );
6282
6283        // Ensure we don't panic when selections are refreshed and that the pending selection is finalized.
6284        editor.change_selections(None, cx, |s| s.refresh());
6285        assert_eq!(
6286            editor.selections.ranges(cx),
6287            [Point::new(0, 3)..Point::new(0, 3)]
6288        );
6289        assert!(editor.selections.pending_anchor().is_some());
6290    });
6291}
6292
6293#[gpui::test]
6294async fn test_extra_newline_insertion(cx: &mut gpui::TestAppContext) {
6295    init_test(cx, |_| {});
6296
6297    let language = Arc::new(
6298        Language::new(
6299            LanguageConfig {
6300                brackets: BracketPairConfig {
6301                    pairs: vec![
6302                        BracketPair {
6303                            start: "{".to_string(),
6304                            end: "}".to_string(),
6305                            close: true,
6306                            newline: true,
6307                        },
6308                        BracketPair {
6309                            start: "/* ".to_string(),
6310                            end: " */".to_string(),
6311                            close: true,
6312                            newline: true,
6313                        },
6314                    ],
6315                    ..Default::default()
6316                },
6317                ..Default::default()
6318            },
6319            Some(tree_sitter_rust::language()),
6320        )
6321        .with_indents_query("")
6322        .unwrap(),
6323    );
6324
6325    let text = concat!(
6326        "{   }\n",     //
6327        "  x\n",       //
6328        "  /*   */\n", //
6329        "x\n",         //
6330        "{{} }\n",     //
6331    );
6332
6333    let buffer = cx
6334        .new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), text).with_language(language, cx));
6335    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
6336    let (view, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
6337    view.condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
6338        .await;
6339
6340    _ = view.update(cx, |view, cx| {
6341        view.change_selections(None, cx, |s| {
6342            s.select_display_ranges([
6343                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 3),
6344                DisplayPoint::new(2, 5)..DisplayPoint::new(2, 5),
6345                DisplayPoint::new(4, 4)..DisplayPoint::new(4, 4),
6346            ])
6347        });
6348        view.newline(&Newline, cx);
6349
6350        assert_eq!(
6351            view.buffer().read(cx).read(cx).text(),
6352            concat!(
6353                "{ \n",    // Suppress rustfmt
6354                "\n",      //
6355                "}\n",     //
6356                "  x\n",   //
6357                "  /* \n", //
6358                "  \n",    //
6359                "  */\n",  //
6360                "x\n",     //
6361                "{{} \n",  //
6362                "}\n",     //
6363            )
6364        );
6365    });
6366}
6367
6368#[gpui::test]
6369fn test_highlighted_ranges(cx: &mut TestAppContext) {
6370    init_test(cx, |_| {});
6371
6372    let editor = cx.add_window(|cx| {
6373        let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
6374        build_editor(buffer.clone(), cx)
6375    });
6376
6377    _ = editor.update(cx, |editor, cx| {
6378        struct Type1;
6379        struct Type2;
6380
6381        let buffer = editor.buffer.read(cx).snapshot(cx);
6382
6383        let anchor_range =
6384            |range: Range<Point>| buffer.anchor_after(range.start)..buffer.anchor_after(range.end);
6385
6386        editor.highlight_background::<Type1>(
6387            vec![
6388                anchor_range(Point::new(2, 1)..Point::new(2, 3)),
6389                anchor_range(Point::new(4, 2)..Point::new(4, 4)),
6390                anchor_range(Point::new(6, 3)..Point::new(6, 5)),
6391                anchor_range(Point::new(8, 4)..Point::new(8, 6)),
6392            ],
6393            |_| Hsla::red(),
6394            cx,
6395        );
6396        editor.highlight_background::<Type2>(
6397            vec![
6398                anchor_range(Point::new(3, 2)..Point::new(3, 5)),
6399                anchor_range(Point::new(5, 3)..Point::new(5, 6)),
6400                anchor_range(Point::new(7, 4)..Point::new(7, 7)),
6401                anchor_range(Point::new(9, 5)..Point::new(9, 8)),
6402            ],
6403            |_| Hsla::green(),
6404            cx,
6405        );
6406
6407        let snapshot = editor.snapshot(cx);
6408        let mut highlighted_ranges = editor.background_highlights_in_range(
6409            anchor_range(Point::new(3, 4)..Point::new(7, 4)),
6410            &snapshot,
6411            cx.theme().colors(),
6412        );
6413        // Enforce a consistent ordering based on color without relying on the ordering of the
6414        // highlight's `TypeId` which is non-executor.
6415        highlighted_ranges.sort_unstable_by_key(|(_, color)| *color);
6416        assert_eq!(
6417            highlighted_ranges,
6418            &[
6419                (
6420                    DisplayPoint::new(4, 2)..DisplayPoint::new(4, 4),
6421                    Hsla::red(),
6422                ),
6423                (
6424                    DisplayPoint::new(6, 3)..DisplayPoint::new(6, 5),
6425                    Hsla::red(),
6426                ),
6427                (
6428                    DisplayPoint::new(3, 2)..DisplayPoint::new(3, 5),
6429                    Hsla::green(),
6430                ),
6431                (
6432                    DisplayPoint::new(5, 3)..DisplayPoint::new(5, 6),
6433                    Hsla::green(),
6434                ),
6435            ]
6436        );
6437        assert_eq!(
6438            editor.background_highlights_in_range(
6439                anchor_range(Point::new(5, 6)..Point::new(6, 4)),
6440                &snapshot,
6441                cx.theme().colors(),
6442            ),
6443            &[(
6444                DisplayPoint::new(6, 3)..DisplayPoint::new(6, 5),
6445                Hsla::red(),
6446            )]
6447        );
6448    });
6449}
6450
6451#[gpui::test]
6452async fn test_following(cx: &mut gpui::TestAppContext) {
6453    init_test(cx, |_| {});
6454
6455    let fs = FakeFs::new(cx.executor());
6456    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
6457
6458    let buffer = project.update(cx, |project, cx| {
6459        let buffer = project
6460            .create_buffer(&sample_text(16, 8, 'a'), None, cx)
6461            .unwrap();
6462        cx.new_model(|cx| MultiBuffer::singleton(buffer, cx))
6463    });
6464    let leader = cx.add_window(|cx| build_editor(buffer.clone(), cx));
6465    let follower = cx.update(|cx| {
6466        cx.open_window(
6467            WindowOptions {
6468                bounds: WindowBounds::Fixed(Bounds::from_corners(
6469                    gpui::Point::new((0. as f64).into(), (0. as f64).into()),
6470                    gpui::Point::new((10. as f64).into(), (80. as f64).into()),
6471                )),
6472                ..Default::default()
6473            },
6474            |cx| cx.new_view(|cx| build_editor(buffer.clone(), cx)),
6475        )
6476    });
6477
6478    let is_still_following = Rc::new(RefCell::new(true));
6479    let follower_edit_event_count = Rc::new(RefCell::new(0));
6480    let pending_update = Rc::new(RefCell::new(None));
6481    _ = follower.update(cx, {
6482        let update = pending_update.clone();
6483        let is_still_following = is_still_following.clone();
6484        let follower_edit_event_count = follower_edit_event_count.clone();
6485        |_, cx| {
6486            cx.subscribe(
6487                &leader.root_view(cx).unwrap(),
6488                move |_, leader, event, cx| {
6489                    leader
6490                        .read(cx)
6491                        .add_event_to_update_proto(event, &mut *update.borrow_mut(), cx);
6492                },
6493            )
6494            .detach();
6495
6496            cx.subscribe(
6497                &follower.root_view(cx).unwrap(),
6498                move |_, _, event: &EditorEvent, _cx| {
6499                    if matches!(Editor::to_follow_event(event), Some(FollowEvent::Unfollow)) {
6500                        *is_still_following.borrow_mut() = false;
6501                    }
6502
6503                    if let EditorEvent::BufferEdited = event {
6504                        *follower_edit_event_count.borrow_mut() += 1;
6505                    }
6506                },
6507            )
6508            .detach();
6509        }
6510    });
6511
6512    // Update the selections only
6513    _ = leader.update(cx, |leader, cx| {
6514        leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
6515    });
6516    follower
6517        .update(cx, |follower, cx| {
6518            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
6519        })
6520        .unwrap()
6521        .await
6522        .unwrap();
6523    _ = follower.update(cx, |follower, cx| {
6524        assert_eq!(follower.selections.ranges(cx), vec![1..1]);
6525    });
6526    assert_eq!(*is_still_following.borrow(), true);
6527    assert_eq!(*follower_edit_event_count.borrow(), 0);
6528
6529    // Update the scroll position only
6530    _ = leader.update(cx, |leader, cx| {
6531        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), cx);
6532    });
6533    follower
6534        .update(cx, |follower, cx| {
6535            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
6536        })
6537        .unwrap()
6538        .await
6539        .unwrap();
6540    assert_eq!(
6541        follower
6542            .update(cx, |follower, cx| follower.scroll_position(cx))
6543            .unwrap(),
6544        gpui::Point::new(1.5, 3.5)
6545    );
6546    assert_eq!(*is_still_following.borrow(), true);
6547    assert_eq!(*follower_edit_event_count.borrow(), 0);
6548
6549    // Update the selections and scroll position. The follower's scroll position is updated
6550    // via autoscroll, not via the leader's exact scroll position.
6551    _ = leader.update(cx, |leader, cx| {
6552        leader.change_selections(None, cx, |s| s.select_ranges([0..0]));
6553        leader.request_autoscroll(Autoscroll::newest(), cx);
6554        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), cx);
6555    });
6556    follower
6557        .update(cx, |follower, cx| {
6558            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
6559        })
6560        .unwrap()
6561        .await
6562        .unwrap();
6563    _ = follower.update(cx, |follower, cx| {
6564        assert_eq!(follower.scroll_position(cx), gpui::Point::new(1.5, 0.0));
6565        assert_eq!(follower.selections.ranges(cx), vec![0..0]);
6566    });
6567    assert_eq!(*is_still_following.borrow(), true);
6568
6569    // Creating a pending selection that precedes another selection
6570    _ = leader.update(cx, |leader, cx| {
6571        leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
6572        leader.begin_selection(DisplayPoint::new(0, 0), true, 1, cx);
6573    });
6574    follower
6575        .update(cx, |follower, cx| {
6576            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
6577        })
6578        .unwrap()
6579        .await
6580        .unwrap();
6581    _ = follower.update(cx, |follower, cx| {
6582        assert_eq!(follower.selections.ranges(cx), vec![0..0, 1..1]);
6583    });
6584    assert_eq!(*is_still_following.borrow(), true);
6585
6586    // Extend the pending selection so that it surrounds another selection
6587    _ = leader.update(cx, |leader, cx| {
6588        leader.extend_selection(DisplayPoint::new(0, 2), 1, cx);
6589    });
6590    follower
6591        .update(cx, |follower, cx| {
6592            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
6593        })
6594        .unwrap()
6595        .await
6596        .unwrap();
6597    _ = follower.update(cx, |follower, cx| {
6598        assert_eq!(follower.selections.ranges(cx), vec![0..2]);
6599    });
6600
6601    // Scrolling locally breaks the follow
6602    _ = follower.update(cx, |follower, cx| {
6603        let top_anchor = follower.buffer().read(cx).read(cx).anchor_after(0);
6604        follower.set_scroll_anchor(
6605            ScrollAnchor {
6606                anchor: top_anchor,
6607                offset: gpui::Point::new(0.0, 0.5),
6608            },
6609            cx,
6610        );
6611    });
6612    assert_eq!(*is_still_following.borrow(), false);
6613}
6614
6615#[gpui::test]
6616async fn test_following_with_multiple_excerpts(cx: &mut gpui::TestAppContext) {
6617    init_test(cx, |_| {});
6618
6619    let fs = FakeFs::new(cx.executor());
6620    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
6621    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
6622    let pane = workspace
6623        .update(cx, |workspace, _| workspace.active_pane().clone())
6624        .unwrap();
6625
6626    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
6627
6628    let leader = pane.update(cx, |_, cx| {
6629        let multibuffer = cx.new_model(|_| MultiBuffer::new(0, ReadWrite));
6630        cx.new_view(|cx| build_editor(multibuffer.clone(), cx))
6631    });
6632
6633    // Start following the editor when it has no excerpts.
6634    let mut state_message = leader.update(cx, |leader, cx| leader.to_state_proto(cx));
6635    let follower_1 = cx
6636        .update_window(*workspace.deref(), |_, cx| {
6637            Editor::from_state_proto(
6638                pane.clone(),
6639                workspace.root_view(cx).unwrap(),
6640                ViewId {
6641                    creator: Default::default(),
6642                    id: 0,
6643                },
6644                &mut state_message,
6645                cx,
6646            )
6647        })
6648        .unwrap()
6649        .unwrap()
6650        .await
6651        .unwrap();
6652
6653    let update_message = Rc::new(RefCell::new(None));
6654    follower_1.update(cx, {
6655        let update = update_message.clone();
6656        |_, cx| {
6657            cx.subscribe(&leader, move |_, leader, event, cx| {
6658                leader
6659                    .read(cx)
6660                    .add_event_to_update_proto(event, &mut *update.borrow_mut(), cx);
6661            })
6662            .detach();
6663        }
6664    });
6665
6666    let (buffer_1, buffer_2) = project.update(cx, |project, cx| {
6667        (
6668            project
6669                .create_buffer("abc\ndef\nghi\njkl\n", None, cx)
6670                .unwrap(),
6671            project
6672                .create_buffer("mno\npqr\nstu\nvwx\n", None, cx)
6673                .unwrap(),
6674        )
6675    });
6676
6677    // Insert some excerpts.
6678    _ = leader.update(cx, |leader, cx| {
6679        leader.buffer.update(cx, |multibuffer, cx| {
6680            let excerpt_ids = multibuffer.push_excerpts(
6681                buffer_1.clone(),
6682                [
6683                    ExcerptRange {
6684                        context: 1..6,
6685                        primary: None,
6686                    },
6687                    ExcerptRange {
6688                        context: 12..15,
6689                        primary: None,
6690                    },
6691                    ExcerptRange {
6692                        context: 0..3,
6693                        primary: None,
6694                    },
6695                ],
6696                cx,
6697            );
6698            multibuffer.insert_excerpts_after(
6699                excerpt_ids[0],
6700                buffer_2.clone(),
6701                [
6702                    ExcerptRange {
6703                        context: 8..12,
6704                        primary: None,
6705                    },
6706                    ExcerptRange {
6707                        context: 0..6,
6708                        primary: None,
6709                    },
6710                ],
6711                cx,
6712            );
6713        });
6714    });
6715
6716    // Apply the update of adding the excerpts.
6717    follower_1
6718        .update(cx, |follower, cx| {
6719            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
6720        })
6721        .await
6722        .unwrap();
6723    assert_eq!(
6724        follower_1.update(cx, |editor, cx| editor.text(cx)),
6725        leader.update(cx, |editor, cx| editor.text(cx))
6726    );
6727    update_message.borrow_mut().take();
6728
6729    // Start following separately after it already has excerpts.
6730    let mut state_message = leader.update(cx, |leader, cx| leader.to_state_proto(cx));
6731    let follower_2 = cx
6732        .update_window(*workspace.deref(), |_, cx| {
6733            Editor::from_state_proto(
6734                pane.clone(),
6735                workspace.root_view(cx).unwrap().clone(),
6736                ViewId {
6737                    creator: Default::default(),
6738                    id: 0,
6739                },
6740                &mut state_message,
6741                cx,
6742            )
6743        })
6744        .unwrap()
6745        .unwrap()
6746        .await
6747        .unwrap();
6748    assert_eq!(
6749        follower_2.update(cx, |editor, cx| editor.text(cx)),
6750        leader.update(cx, |editor, cx| editor.text(cx))
6751    );
6752
6753    // Remove some excerpts.
6754    _ = leader.update(cx, |leader, cx| {
6755        leader.buffer.update(cx, |multibuffer, cx| {
6756            let excerpt_ids = multibuffer.excerpt_ids();
6757            multibuffer.remove_excerpts([excerpt_ids[1], excerpt_ids[2]], cx);
6758            multibuffer.remove_excerpts([excerpt_ids[0]], cx);
6759        });
6760    });
6761
6762    // Apply the update of removing the excerpts.
6763    follower_1
6764        .update(cx, |follower, cx| {
6765            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
6766        })
6767        .await
6768        .unwrap();
6769    follower_2
6770        .update(cx, |follower, cx| {
6771            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
6772        })
6773        .await
6774        .unwrap();
6775    update_message.borrow_mut().take();
6776    assert_eq!(
6777        follower_1.update(cx, |editor, cx| editor.text(cx)),
6778        leader.update(cx, |editor, cx| editor.text(cx))
6779    );
6780}
6781
6782#[gpui::test]
6783async fn go_to_prev_overlapping_diagnostic(
6784    executor: BackgroundExecutor,
6785    cx: &mut gpui::TestAppContext,
6786) {
6787    init_test(cx, |_| {});
6788
6789    let mut cx = EditorTestContext::new(cx).await;
6790    let project = cx.update_editor(|editor, _| editor.project.clone().unwrap());
6791
6792    cx.set_state(indoc! {"
6793        ˇfn func(abc def: i32) -> u32 {
6794        }
6795    "});
6796
6797    _ = cx.update(|cx| {
6798        _ = project.update(cx, |project, cx| {
6799            project
6800                .update_diagnostics(
6801                    LanguageServerId(0),
6802                    lsp::PublishDiagnosticsParams {
6803                        uri: lsp::Url::from_file_path("/root/file").unwrap(),
6804                        version: None,
6805                        diagnostics: vec![
6806                            lsp::Diagnostic {
6807                                range: lsp::Range::new(
6808                                    lsp::Position::new(0, 11),
6809                                    lsp::Position::new(0, 12),
6810                                ),
6811                                severity: Some(lsp::DiagnosticSeverity::ERROR),
6812                                ..Default::default()
6813                            },
6814                            lsp::Diagnostic {
6815                                range: lsp::Range::new(
6816                                    lsp::Position::new(0, 12),
6817                                    lsp::Position::new(0, 15),
6818                                ),
6819                                severity: Some(lsp::DiagnosticSeverity::ERROR),
6820                                ..Default::default()
6821                            },
6822                            lsp::Diagnostic {
6823                                range: lsp::Range::new(
6824                                    lsp::Position::new(0, 25),
6825                                    lsp::Position::new(0, 28),
6826                                ),
6827                                severity: Some(lsp::DiagnosticSeverity::ERROR),
6828                                ..Default::default()
6829                            },
6830                        ],
6831                    },
6832                    &[],
6833                    cx,
6834                )
6835                .unwrap()
6836        });
6837    });
6838
6839    executor.run_until_parked();
6840
6841    cx.update_editor(|editor, cx| {
6842        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
6843    });
6844
6845    cx.assert_editor_state(indoc! {"
6846        fn func(abc def: i32) -> ˇu32 {
6847        }
6848    "});
6849
6850    cx.update_editor(|editor, cx| {
6851        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
6852    });
6853
6854    cx.assert_editor_state(indoc! {"
6855        fn func(abc ˇdef: i32) -> u32 {
6856        }
6857    "});
6858
6859    cx.update_editor(|editor, cx| {
6860        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
6861    });
6862
6863    cx.assert_editor_state(indoc! {"
6864        fn func(abcˇ def: i32) -> u32 {
6865        }
6866    "});
6867
6868    cx.update_editor(|editor, cx| {
6869        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
6870    });
6871
6872    cx.assert_editor_state(indoc! {"
6873        fn func(abc def: i32) -> ˇu32 {
6874        }
6875    "});
6876}
6877
6878#[gpui::test]
6879async fn go_to_hunk(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
6880    init_test(cx, |_| {});
6881
6882    let mut cx = EditorTestContext::new(cx).await;
6883
6884    let diff_base = r#"
6885        use some::mod;
6886
6887        const A: u32 = 42;
6888
6889        fn main() {
6890            println!("hello");
6891
6892            println!("world");
6893        }
6894        "#
6895    .unindent();
6896
6897    // Edits are modified, removed, modified, added
6898    cx.set_state(
6899        &r#"
6900        use some::modified;
6901
6902        ˇ
6903        fn main() {
6904            println!("hello there");
6905
6906            println!("around the");
6907            println!("world");
6908        }
6909        "#
6910        .unindent(),
6911    );
6912
6913    cx.set_diff_base(Some(&diff_base));
6914    executor.run_until_parked();
6915
6916    cx.update_editor(|editor, cx| {
6917        //Wrap around the bottom of the buffer
6918        for _ in 0..3 {
6919            editor.go_to_hunk(&GoToHunk, cx);
6920        }
6921    });
6922
6923    cx.assert_editor_state(
6924        &r#"
6925        ˇuse some::modified;
6926
6927
6928        fn main() {
6929            println!("hello there");
6930
6931            println!("around the");
6932            println!("world");
6933        }
6934        "#
6935        .unindent(),
6936    );
6937
6938    cx.update_editor(|editor, cx| {
6939        //Wrap around the top of the buffer
6940        for _ in 0..2 {
6941            editor.go_to_prev_hunk(&GoToPrevHunk, cx);
6942        }
6943    });
6944
6945    cx.assert_editor_state(
6946        &r#"
6947        use some::modified;
6948
6949
6950        fn main() {
6951        ˇ    println!("hello there");
6952
6953            println!("around the");
6954            println!("world");
6955        }
6956        "#
6957        .unindent(),
6958    );
6959
6960    cx.update_editor(|editor, cx| {
6961        editor.go_to_prev_hunk(&GoToPrevHunk, cx);
6962    });
6963
6964    cx.assert_editor_state(
6965        &r#"
6966        use some::modified;
6967
6968        ˇ
6969        fn main() {
6970            println!("hello there");
6971
6972            println!("around the");
6973            println!("world");
6974        }
6975        "#
6976        .unindent(),
6977    );
6978
6979    cx.update_editor(|editor, cx| {
6980        for _ in 0..3 {
6981            editor.go_to_prev_hunk(&GoToPrevHunk, cx);
6982        }
6983    });
6984
6985    cx.assert_editor_state(
6986        &r#"
6987        use some::modified;
6988
6989
6990        fn main() {
6991        ˇ    println!("hello there");
6992
6993            println!("around the");
6994            println!("world");
6995        }
6996        "#
6997        .unindent(),
6998    );
6999
7000    cx.update_editor(|editor, cx| {
7001        editor.fold(&Fold, cx);
7002
7003        //Make sure that the fold only gets one hunk
7004        for _ in 0..4 {
7005            editor.go_to_hunk(&GoToHunk, cx);
7006        }
7007    });
7008
7009    cx.assert_editor_state(
7010        &r#"
7011        ˇuse some::modified;
7012
7013
7014        fn main() {
7015            println!("hello there");
7016
7017            println!("around the");
7018            println!("world");
7019        }
7020        "#
7021        .unindent(),
7022    );
7023}
7024
7025#[test]
7026fn test_split_words() {
7027    fn split<'a>(text: &'a str) -> Vec<&'a str> {
7028        split_words(text).collect()
7029    }
7030
7031    assert_eq!(split("HelloWorld"), &["Hello", "World"]);
7032    assert_eq!(split("hello_world"), &["hello_", "world"]);
7033    assert_eq!(split("_hello_world_"), &["_", "hello_", "world_"]);
7034    assert_eq!(split("Hello_World"), &["Hello_", "World"]);
7035    assert_eq!(split("helloWOrld"), &["hello", "WOrld"]);
7036    assert_eq!(split("helloworld"), &["helloworld"]);
7037}
7038
7039#[gpui::test]
7040async fn test_move_to_enclosing_bracket(cx: &mut gpui::TestAppContext) {
7041    init_test(cx, |_| {});
7042
7043    let mut cx = EditorLspTestContext::new_typescript(Default::default(), cx).await;
7044    let mut assert = |before, after| {
7045        let _state_context = cx.set_state(before);
7046        cx.update_editor(|editor, cx| {
7047            editor.move_to_enclosing_bracket(&MoveToEnclosingBracket, cx)
7048        });
7049        cx.assert_editor_state(after);
7050    };
7051
7052    // Outside bracket jumps to outside of matching bracket
7053    assert("console.logˇ(var);", "console.log(var)ˇ;");
7054    assert("console.log(var)ˇ;", "console.logˇ(var);");
7055
7056    // Inside bracket jumps to inside of matching bracket
7057    assert("console.log(ˇvar);", "console.log(varˇ);");
7058    assert("console.log(varˇ);", "console.log(ˇvar);");
7059
7060    // When outside a bracket and inside, favor jumping to the inside bracket
7061    assert(
7062        "console.log('foo', [1, 2, 3]ˇ);",
7063        "console.log(ˇ'foo', [1, 2, 3]);",
7064    );
7065    assert(
7066        "console.log(ˇ'foo', [1, 2, 3]);",
7067        "console.log('foo', [1, 2, 3]ˇ);",
7068    );
7069
7070    // Bias forward if two options are equally likely
7071    assert(
7072        "let result = curried_fun()ˇ();",
7073        "let result = curried_fun()()ˇ;",
7074    );
7075
7076    // If directly adjacent to a smaller pair but inside a larger (not adjacent), pick the smaller
7077    assert(
7078        indoc! {"
7079            function test() {
7080                console.log('test')ˇ
7081            }"},
7082        indoc! {"
7083            function test() {
7084                console.logˇ('test')
7085            }"},
7086    );
7087}
7088
7089#[gpui::test(iterations = 10)]
7090async fn test_copilot(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
7091    // flaky
7092    init_test(cx, |_| {});
7093
7094    let (copilot, copilot_lsp) = Copilot::fake(cx);
7095    _ = cx.update(|cx| cx.set_global(copilot));
7096    let mut cx = EditorLspTestContext::new_rust(
7097        lsp::ServerCapabilities {
7098            completion_provider: Some(lsp::CompletionOptions {
7099                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
7100                ..Default::default()
7101            }),
7102            ..Default::default()
7103        },
7104        cx,
7105    )
7106    .await;
7107
7108    // When inserting, ensure autocompletion is favored over Copilot suggestions.
7109    cx.set_state(indoc! {"
7110        oneˇ
7111        two
7112        three
7113    "});
7114    cx.simulate_keystroke(".");
7115    let _ = handle_completion_request(
7116        &mut cx,
7117        indoc! {"
7118            one.|<>
7119            two
7120            three
7121        "},
7122        vec!["completion_a", "completion_b"],
7123    );
7124    handle_copilot_completion_request(
7125        &copilot_lsp,
7126        vec![copilot::request::Completion {
7127            text: "one.copilot1".into(),
7128            range: lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 4)),
7129            ..Default::default()
7130        }],
7131        vec![],
7132    );
7133    executor.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
7134    cx.update_editor(|editor, cx| {
7135        assert!(editor.context_menu_visible());
7136        assert!(!editor.has_active_copilot_suggestion(cx));
7137
7138        // Confirming a completion inserts it and hides the context menu, without showing
7139        // the copilot suggestion afterwards.
7140        editor
7141            .confirm_completion(&Default::default(), cx)
7142            .unwrap()
7143            .detach();
7144        assert!(!editor.context_menu_visible());
7145        assert!(!editor.has_active_copilot_suggestion(cx));
7146        assert_eq!(editor.text(cx), "one.completion_a\ntwo\nthree\n");
7147        assert_eq!(editor.display_text(cx), "one.completion_a\ntwo\nthree\n");
7148    });
7149
7150    // Ensure Copilot suggestions are shown right away if no autocompletion is available.
7151    cx.set_state(indoc! {"
7152        oneˇ
7153        two
7154        three
7155    "});
7156    cx.simulate_keystroke(".");
7157    let _ = handle_completion_request(
7158        &mut cx,
7159        indoc! {"
7160            one.|<>
7161            two
7162            three
7163        "},
7164        vec![],
7165    );
7166    handle_copilot_completion_request(
7167        &copilot_lsp,
7168        vec![copilot::request::Completion {
7169            text: "one.copilot1".into(),
7170            range: lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 4)),
7171            ..Default::default()
7172        }],
7173        vec![],
7174    );
7175    executor.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
7176    cx.update_editor(|editor, cx| {
7177        assert!(!editor.context_menu_visible());
7178        assert!(editor.has_active_copilot_suggestion(cx));
7179        assert_eq!(editor.display_text(cx), "one.copilot1\ntwo\nthree\n");
7180        assert_eq!(editor.text(cx), "one.\ntwo\nthree\n");
7181    });
7182
7183    // Reset editor, and ensure autocompletion is still favored over Copilot suggestions.
7184    cx.set_state(indoc! {"
7185        oneˇ
7186        two
7187        three
7188    "});
7189    cx.simulate_keystroke(".");
7190    let _ = handle_completion_request(
7191        &mut cx,
7192        indoc! {"
7193            one.|<>
7194            two
7195            three
7196        "},
7197        vec!["completion_a", "completion_b"],
7198    );
7199    handle_copilot_completion_request(
7200        &copilot_lsp,
7201        vec![copilot::request::Completion {
7202            text: "one.copilot1".into(),
7203            range: lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 4)),
7204            ..Default::default()
7205        }],
7206        vec![],
7207    );
7208    executor.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
7209    cx.update_editor(|editor, cx| {
7210        assert!(editor.context_menu_visible());
7211        assert!(!editor.has_active_copilot_suggestion(cx));
7212
7213        // When hiding the context menu, the Copilot suggestion becomes visible.
7214        editor.hide_context_menu(cx);
7215        assert!(!editor.context_menu_visible());
7216        assert!(editor.has_active_copilot_suggestion(cx));
7217        assert_eq!(editor.display_text(cx), "one.copilot1\ntwo\nthree\n");
7218        assert_eq!(editor.text(cx), "one.\ntwo\nthree\n");
7219    });
7220
7221    // Ensure existing completion is interpolated when inserting again.
7222    cx.simulate_keystroke("c");
7223    executor.run_until_parked();
7224    cx.update_editor(|editor, cx| {
7225        assert!(!editor.context_menu_visible());
7226        assert!(editor.has_active_copilot_suggestion(cx));
7227        assert_eq!(editor.display_text(cx), "one.copilot1\ntwo\nthree\n");
7228        assert_eq!(editor.text(cx), "one.c\ntwo\nthree\n");
7229    });
7230
7231    // After debouncing, new Copilot completions should be requested.
7232    handle_copilot_completion_request(
7233        &copilot_lsp,
7234        vec![copilot::request::Completion {
7235            text: "one.copilot2".into(),
7236            range: lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 5)),
7237            ..Default::default()
7238        }],
7239        vec![],
7240    );
7241    executor.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
7242    cx.update_editor(|editor, cx| {
7243        assert!(!editor.context_menu_visible());
7244        assert!(editor.has_active_copilot_suggestion(cx));
7245        assert_eq!(editor.display_text(cx), "one.copilot2\ntwo\nthree\n");
7246        assert_eq!(editor.text(cx), "one.c\ntwo\nthree\n");
7247
7248        // Canceling should remove the active Copilot suggestion.
7249        editor.cancel(&Default::default(), cx);
7250        assert!(!editor.has_active_copilot_suggestion(cx));
7251        assert_eq!(editor.display_text(cx), "one.c\ntwo\nthree\n");
7252        assert_eq!(editor.text(cx), "one.c\ntwo\nthree\n");
7253
7254        // After canceling, tabbing shouldn't insert the previously shown suggestion.
7255        editor.tab(&Default::default(), cx);
7256        assert!(!editor.has_active_copilot_suggestion(cx));
7257        assert_eq!(editor.display_text(cx), "one.c   \ntwo\nthree\n");
7258        assert_eq!(editor.text(cx), "one.c   \ntwo\nthree\n");
7259
7260        // When undoing the previously active suggestion is shown again.
7261        editor.undo(&Default::default(), cx);
7262        assert!(editor.has_active_copilot_suggestion(cx));
7263        assert_eq!(editor.display_text(cx), "one.copilot2\ntwo\nthree\n");
7264        assert_eq!(editor.text(cx), "one.c\ntwo\nthree\n");
7265    });
7266
7267    // If an edit occurs outside of this editor, the suggestion is still correctly interpolated.
7268    cx.update_buffer(|buffer, cx| buffer.edit([(5..5, "o")], None, cx));
7269    cx.update_editor(|editor, cx| {
7270        assert!(editor.has_active_copilot_suggestion(cx));
7271        assert_eq!(editor.display_text(cx), "one.copilot2\ntwo\nthree\n");
7272        assert_eq!(editor.text(cx), "one.co\ntwo\nthree\n");
7273
7274        // Tabbing when there is an active suggestion inserts it.
7275        editor.tab(&Default::default(), cx);
7276        assert!(!editor.has_active_copilot_suggestion(cx));
7277        assert_eq!(editor.display_text(cx), "one.copilot2\ntwo\nthree\n");
7278        assert_eq!(editor.text(cx), "one.copilot2\ntwo\nthree\n");
7279
7280        // When undoing the previously active suggestion is shown again.
7281        editor.undo(&Default::default(), cx);
7282        assert!(editor.has_active_copilot_suggestion(cx));
7283        assert_eq!(editor.display_text(cx), "one.copilot2\ntwo\nthree\n");
7284        assert_eq!(editor.text(cx), "one.co\ntwo\nthree\n");
7285
7286        // Hide suggestion.
7287        editor.cancel(&Default::default(), cx);
7288        assert!(!editor.has_active_copilot_suggestion(cx));
7289        assert_eq!(editor.display_text(cx), "one.co\ntwo\nthree\n");
7290        assert_eq!(editor.text(cx), "one.co\ntwo\nthree\n");
7291    });
7292
7293    // If an edit occurs outside of this editor but no suggestion is being shown,
7294    // we won't make it visible.
7295    cx.update_buffer(|buffer, cx| buffer.edit([(6..6, "p")], None, cx));
7296    cx.update_editor(|editor, cx| {
7297        assert!(!editor.has_active_copilot_suggestion(cx));
7298        assert_eq!(editor.display_text(cx), "one.cop\ntwo\nthree\n");
7299        assert_eq!(editor.text(cx), "one.cop\ntwo\nthree\n");
7300    });
7301
7302    // Reset the editor to verify how suggestions behave when tabbing on leading indentation.
7303    cx.update_editor(|editor, cx| {
7304        editor.set_text("fn foo() {\n  \n}", cx);
7305        editor.change_selections(None, cx, |s| {
7306            s.select_ranges([Point::new(1, 2)..Point::new(1, 2)])
7307        });
7308    });
7309    handle_copilot_completion_request(
7310        &copilot_lsp,
7311        vec![copilot::request::Completion {
7312            text: "    let x = 4;".into(),
7313            range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 2)),
7314            ..Default::default()
7315        }],
7316        vec![],
7317    );
7318
7319    cx.update_editor(|editor, cx| editor.next_copilot_suggestion(&Default::default(), cx));
7320    executor.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
7321    cx.update_editor(|editor, cx| {
7322        assert!(editor.has_active_copilot_suggestion(cx));
7323        assert_eq!(editor.display_text(cx), "fn foo() {\n    let x = 4;\n}");
7324        assert_eq!(editor.text(cx), "fn foo() {\n  \n}");
7325
7326        // Tabbing inside of leading whitespace inserts indentation without accepting the suggestion.
7327        editor.tab(&Default::default(), cx);
7328        assert!(editor.has_active_copilot_suggestion(cx));
7329        assert_eq!(editor.text(cx), "fn foo() {\n    \n}");
7330        assert_eq!(editor.display_text(cx), "fn foo() {\n    let x = 4;\n}");
7331
7332        // Tabbing again accepts the suggestion.
7333        editor.tab(&Default::default(), cx);
7334        assert!(!editor.has_active_copilot_suggestion(cx));
7335        assert_eq!(editor.text(cx), "fn foo() {\n    let x = 4;\n}");
7336        assert_eq!(editor.display_text(cx), "fn foo() {\n    let x = 4;\n}");
7337    });
7338}
7339
7340#[gpui::test]
7341async fn test_copilot_completion_invalidation(
7342    executor: BackgroundExecutor,
7343    cx: &mut gpui::TestAppContext,
7344) {
7345    init_test(cx, |_| {});
7346
7347    let (copilot, copilot_lsp) = Copilot::fake(cx);
7348    _ = cx.update(|cx| cx.set_global(copilot));
7349    let mut cx = EditorLspTestContext::new_rust(
7350        lsp::ServerCapabilities {
7351            completion_provider: Some(lsp::CompletionOptions {
7352                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
7353                ..Default::default()
7354            }),
7355            ..Default::default()
7356        },
7357        cx,
7358    )
7359    .await;
7360
7361    cx.set_state(indoc! {"
7362        one
7363        twˇ
7364        three
7365    "});
7366
7367    handle_copilot_completion_request(
7368        &copilot_lsp,
7369        vec![copilot::request::Completion {
7370            text: "two.foo()".into(),
7371            range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 2)),
7372            ..Default::default()
7373        }],
7374        vec![],
7375    );
7376    cx.update_editor(|editor, cx| editor.next_copilot_suggestion(&Default::default(), cx));
7377    executor.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
7378    cx.update_editor(|editor, cx| {
7379        assert!(editor.has_active_copilot_suggestion(cx));
7380        assert_eq!(editor.display_text(cx), "one\ntwo.foo()\nthree\n");
7381        assert_eq!(editor.text(cx), "one\ntw\nthree\n");
7382
7383        editor.backspace(&Default::default(), cx);
7384        assert!(editor.has_active_copilot_suggestion(cx));
7385        assert_eq!(editor.display_text(cx), "one\ntwo.foo()\nthree\n");
7386        assert_eq!(editor.text(cx), "one\nt\nthree\n");
7387
7388        editor.backspace(&Default::default(), cx);
7389        assert!(editor.has_active_copilot_suggestion(cx));
7390        assert_eq!(editor.display_text(cx), "one\ntwo.foo()\nthree\n");
7391        assert_eq!(editor.text(cx), "one\n\nthree\n");
7392
7393        // Deleting across the original suggestion range invalidates it.
7394        editor.backspace(&Default::default(), cx);
7395        assert!(!editor.has_active_copilot_suggestion(cx));
7396        assert_eq!(editor.display_text(cx), "one\nthree\n");
7397        assert_eq!(editor.text(cx), "one\nthree\n");
7398
7399        // Undoing the deletion restores the suggestion.
7400        editor.undo(&Default::default(), cx);
7401        assert!(editor.has_active_copilot_suggestion(cx));
7402        assert_eq!(editor.display_text(cx), "one\ntwo.foo()\nthree\n");
7403        assert_eq!(editor.text(cx), "one\n\nthree\n");
7404    });
7405}
7406
7407#[gpui::test]
7408async fn test_copilot_multibuffer(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
7409    init_test(cx, |_| {});
7410
7411    let (copilot, copilot_lsp) = Copilot::fake(cx);
7412    _ = cx.update(|cx| cx.set_global(copilot));
7413
7414    let buffer_1 = cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), "a = 1\nb = 2\n"));
7415    let buffer_2 = cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), "c = 3\nd = 4\n"));
7416    let multibuffer = cx.new_model(|cx| {
7417        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
7418        multibuffer.push_excerpts(
7419            buffer_1.clone(),
7420            [ExcerptRange {
7421                context: Point::new(0, 0)..Point::new(2, 0),
7422                primary: None,
7423            }],
7424            cx,
7425        );
7426        multibuffer.push_excerpts(
7427            buffer_2.clone(),
7428            [ExcerptRange {
7429                context: Point::new(0, 0)..Point::new(2, 0),
7430                primary: None,
7431            }],
7432            cx,
7433        );
7434        multibuffer
7435    });
7436    let editor = cx.add_window(|cx| build_editor(multibuffer, cx));
7437
7438    handle_copilot_completion_request(
7439        &copilot_lsp,
7440        vec![copilot::request::Completion {
7441            text: "b = 2 + a".into(),
7442            range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 5)),
7443            ..Default::default()
7444        }],
7445        vec![],
7446    );
7447    _ = editor.update(cx, |editor, cx| {
7448        // Ensure copilot suggestions are shown for the first excerpt.
7449        editor.change_selections(None, cx, |s| {
7450            s.select_ranges([Point::new(1, 5)..Point::new(1, 5)])
7451        });
7452        editor.next_copilot_suggestion(&Default::default(), cx);
7453    });
7454    executor.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
7455    _ = editor.update(cx, |editor, cx| {
7456        assert!(editor.has_active_copilot_suggestion(cx));
7457        assert_eq!(
7458            editor.display_text(cx),
7459            "\n\na = 1\nb = 2 + a\n\n\n\nc = 3\nd = 4\n"
7460        );
7461        assert_eq!(editor.text(cx), "a = 1\nb = 2\n\nc = 3\nd = 4\n");
7462    });
7463
7464    handle_copilot_completion_request(
7465        &copilot_lsp,
7466        vec![copilot::request::Completion {
7467            text: "d = 4 + c".into(),
7468            range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 6)),
7469            ..Default::default()
7470        }],
7471        vec![],
7472    );
7473    _ = editor.update(cx, |editor, cx| {
7474        // Move to another excerpt, ensuring the suggestion gets cleared.
7475        editor.change_selections(None, cx, |s| {
7476            s.select_ranges([Point::new(4, 5)..Point::new(4, 5)])
7477        });
7478        assert!(!editor.has_active_copilot_suggestion(cx));
7479        assert_eq!(
7480            editor.display_text(cx),
7481            "\n\na = 1\nb = 2\n\n\n\nc = 3\nd = 4\n"
7482        );
7483        assert_eq!(editor.text(cx), "a = 1\nb = 2\n\nc = 3\nd = 4\n");
7484
7485        // Type a character, ensuring we don't even try to interpolate the previous suggestion.
7486        editor.handle_input(" ", cx);
7487        assert!(!editor.has_active_copilot_suggestion(cx));
7488        assert_eq!(
7489            editor.display_text(cx),
7490            "\n\na = 1\nb = 2\n\n\n\nc = 3\nd = 4 \n"
7491        );
7492        assert_eq!(editor.text(cx), "a = 1\nb = 2\n\nc = 3\nd = 4 \n");
7493    });
7494
7495    // Ensure the new suggestion is displayed when the debounce timeout expires.
7496    executor.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
7497    _ = editor.update(cx, |editor, cx| {
7498        assert!(editor.has_active_copilot_suggestion(cx));
7499        assert_eq!(
7500            editor.display_text(cx),
7501            "\n\na = 1\nb = 2\n\n\n\nc = 3\nd = 4 + c\n"
7502        );
7503        assert_eq!(editor.text(cx), "a = 1\nb = 2\n\nc = 3\nd = 4 \n");
7504    });
7505}
7506
7507#[gpui::test]
7508async fn test_copilot_disabled_globs(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
7509    init_test(cx, |settings| {
7510        settings
7511            .copilot
7512            .get_or_insert(Default::default())
7513            .disabled_globs = Some(vec![".env*".to_string()]);
7514    });
7515
7516    let (copilot, copilot_lsp) = Copilot::fake(cx);
7517    _ = cx.update(|cx| cx.set_global(copilot));
7518
7519    let fs = FakeFs::new(cx.executor());
7520    fs.insert_tree(
7521        "/test",
7522        json!({
7523            ".env": "SECRET=something\n",
7524            "README.md": "hello\n"
7525        }),
7526    )
7527    .await;
7528    let project = Project::test(fs, ["/test".as_ref()], cx).await;
7529
7530    let private_buffer = project
7531        .update(cx, |project, cx| {
7532            project.open_local_buffer("/test/.env", cx)
7533        })
7534        .await
7535        .unwrap();
7536    let public_buffer = project
7537        .update(cx, |project, cx| {
7538            project.open_local_buffer("/test/README.md", cx)
7539        })
7540        .await
7541        .unwrap();
7542
7543    let multibuffer = cx.new_model(|cx| {
7544        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
7545        multibuffer.push_excerpts(
7546            private_buffer.clone(),
7547            [ExcerptRange {
7548                context: Point::new(0, 0)..Point::new(1, 0),
7549                primary: None,
7550            }],
7551            cx,
7552        );
7553        multibuffer.push_excerpts(
7554            public_buffer.clone(),
7555            [ExcerptRange {
7556                context: Point::new(0, 0)..Point::new(1, 0),
7557                primary: None,
7558            }],
7559            cx,
7560        );
7561        multibuffer
7562    });
7563    let editor = cx.add_window(|cx| build_editor(multibuffer, cx));
7564
7565    let mut copilot_requests = copilot_lsp
7566        .handle_request::<copilot::request::GetCompletions, _, _>(move |_params, _cx| async move {
7567            Ok(copilot::request::GetCompletionsResult {
7568                completions: vec![copilot::request::Completion {
7569                    text: "next line".into(),
7570                    range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 0)),
7571                    ..Default::default()
7572                }],
7573            })
7574        });
7575
7576    _ = editor.update(cx, |editor, cx| {
7577        editor.change_selections(None, cx, |selections| {
7578            selections.select_ranges([Point::new(0, 0)..Point::new(0, 0)])
7579        });
7580        editor.next_copilot_suggestion(&Default::default(), cx);
7581    });
7582
7583    executor.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
7584    assert!(copilot_requests.try_next().is_err());
7585
7586    _ = editor.update(cx, |editor, cx| {
7587        editor.change_selections(None, cx, |s| {
7588            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
7589        });
7590        editor.next_copilot_suggestion(&Default::default(), cx);
7591    });
7592
7593    executor.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
7594    assert!(copilot_requests.try_next().is_ok());
7595}
7596
7597#[gpui::test]
7598async fn test_on_type_formatting_not_triggered(cx: &mut gpui::TestAppContext) {
7599    init_test(cx, |_| {});
7600
7601    let mut language = Language::new(
7602        LanguageConfig {
7603            name: "Rust".into(),
7604            path_suffixes: vec!["rs".to_string()],
7605            brackets: BracketPairConfig {
7606                pairs: vec![BracketPair {
7607                    start: "{".to_string(),
7608                    end: "}".to_string(),
7609                    close: true,
7610                    newline: true,
7611                }],
7612                disabled_scopes_by_bracket_ix: Vec::new(),
7613            },
7614            ..Default::default()
7615        },
7616        Some(tree_sitter_rust::language()),
7617    );
7618    let mut fake_servers = language
7619        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
7620            capabilities: lsp::ServerCapabilities {
7621                document_on_type_formatting_provider: Some(lsp::DocumentOnTypeFormattingOptions {
7622                    first_trigger_character: "{".to_string(),
7623                    more_trigger_character: None,
7624                }),
7625                ..Default::default()
7626            },
7627            ..Default::default()
7628        }))
7629        .await;
7630
7631    let fs = FakeFs::new(cx.executor());
7632    fs.insert_tree(
7633        "/a",
7634        json!({
7635            "main.rs": "fn main() { let a = 5; }",
7636            "other.rs": "// Test file",
7637        }),
7638    )
7639    .await;
7640    let project = Project::test(fs, ["/a".as_ref()], cx).await;
7641    _ = project.update(cx, |project, _| project.languages().add(Arc::new(language)));
7642    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
7643
7644    let cx = &mut VisualTestContext::from_window(*workspace, cx);
7645
7646    let worktree_id = workspace
7647        .update(cx, |workspace, cx| {
7648            workspace.project().update(cx, |project, cx| {
7649                project.worktrees().next().unwrap().read(cx).id()
7650            })
7651        })
7652        .unwrap();
7653
7654    let buffer = project
7655        .update(cx, |project, cx| {
7656            project.open_local_buffer("/a/main.rs", cx)
7657        })
7658        .await
7659        .unwrap();
7660    cx.executor().run_until_parked();
7661    cx.executor().start_waiting();
7662    let fake_server = fake_servers.next().await.unwrap();
7663    let editor_handle = workspace
7664        .update(cx, |workspace, cx| {
7665            workspace.open_path((worktree_id, "main.rs"), None, true, cx)
7666        })
7667        .unwrap()
7668        .await
7669        .unwrap()
7670        .downcast::<Editor>()
7671        .unwrap();
7672
7673    fake_server.handle_request::<lsp::request::OnTypeFormatting, _, _>(|params, _| async move {
7674        assert_eq!(
7675            params.text_document_position.text_document.uri,
7676            lsp::Url::from_file_path("/a/main.rs").unwrap(),
7677        );
7678        assert_eq!(
7679            params.text_document_position.position,
7680            lsp::Position::new(0, 21),
7681        );
7682
7683        Ok(Some(vec![lsp::TextEdit {
7684            new_text: "]".to_string(),
7685            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
7686        }]))
7687    });
7688
7689    editor_handle.update(cx, |editor, cx| {
7690        editor.focus(cx);
7691        editor.change_selections(None, cx, |s| {
7692            s.select_ranges([Point::new(0, 21)..Point::new(0, 20)])
7693        });
7694        editor.handle_input("{", cx);
7695    });
7696
7697    cx.executor().run_until_parked();
7698
7699    _ = buffer.update(cx, |buffer, _| {
7700        assert_eq!(
7701            buffer.text(),
7702            "fn main() { let a = {5}; }",
7703            "No extra braces from on type formatting should appear in the buffer"
7704        )
7705    });
7706}
7707
7708#[gpui::test]
7709async fn test_language_server_restart_due_to_settings_change(cx: &mut gpui::TestAppContext) {
7710    init_test(cx, |_| {});
7711
7712    let language_name: Arc<str> = "Rust".into();
7713    let mut language = Language::new(
7714        LanguageConfig {
7715            name: Arc::clone(&language_name),
7716            path_suffixes: vec!["rs".to_string()],
7717            ..Default::default()
7718        },
7719        Some(tree_sitter_rust::language()),
7720    );
7721
7722    let server_restarts = Arc::new(AtomicUsize::new(0));
7723    let closure_restarts = Arc::clone(&server_restarts);
7724    let language_server_name = "test language server";
7725    let mut fake_servers = language
7726        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
7727            name: language_server_name,
7728            initialization_options: Some(json!({
7729                "testOptionValue": true
7730            })),
7731            initializer: Some(Box::new(move |fake_server| {
7732                let task_restarts = Arc::clone(&closure_restarts);
7733                fake_server.handle_request::<lsp::request::Shutdown, _, _>(move |_, _| {
7734                    task_restarts.fetch_add(1, atomic::Ordering::Release);
7735                    futures::future::ready(Ok(()))
7736                });
7737            })),
7738            ..Default::default()
7739        }))
7740        .await;
7741
7742    let fs = FakeFs::new(cx.executor());
7743    fs.insert_tree(
7744        "/a",
7745        json!({
7746            "main.rs": "fn main() { let a = 5; }",
7747            "other.rs": "// Test file",
7748        }),
7749    )
7750    .await;
7751    let project = Project::test(fs, ["/a".as_ref()], cx).await;
7752    _ = project.update(cx, |project, _| project.languages().add(Arc::new(language)));
7753    let _window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
7754    let _buffer = project
7755        .update(cx, |project, cx| {
7756            project.open_local_buffer("/a/main.rs", cx)
7757        })
7758        .await
7759        .unwrap();
7760    let _fake_server = fake_servers.next().await.unwrap();
7761    update_test_language_settings(cx, |language_settings| {
7762        language_settings.languages.insert(
7763            Arc::clone(&language_name),
7764            LanguageSettingsContent {
7765                tab_size: NonZeroU32::new(8),
7766                ..Default::default()
7767            },
7768        );
7769    });
7770    cx.executor().run_until_parked();
7771    assert_eq!(
7772        server_restarts.load(atomic::Ordering::Acquire),
7773        0,
7774        "Should not restart LSP server on an unrelated change"
7775    );
7776
7777    update_test_project_settings(cx, |project_settings| {
7778        project_settings.lsp.insert(
7779            "Some other server name".into(),
7780            LspSettings {
7781                initialization_options: Some(json!({
7782                    "some other init value": false
7783                })),
7784            },
7785        );
7786    });
7787    cx.executor().run_until_parked();
7788    assert_eq!(
7789        server_restarts.load(atomic::Ordering::Acquire),
7790        0,
7791        "Should not restart LSP server on an unrelated LSP settings change"
7792    );
7793
7794    update_test_project_settings(cx, |project_settings| {
7795        project_settings.lsp.insert(
7796            language_server_name.into(),
7797            LspSettings {
7798                initialization_options: Some(json!({
7799                    "anotherInitValue": false
7800                })),
7801            },
7802        );
7803    });
7804    cx.executor().run_until_parked();
7805    assert_eq!(
7806        server_restarts.load(atomic::Ordering::Acquire),
7807        1,
7808        "Should restart LSP server on a related LSP settings change"
7809    );
7810
7811    update_test_project_settings(cx, |project_settings| {
7812        project_settings.lsp.insert(
7813            language_server_name.into(),
7814            LspSettings {
7815                initialization_options: Some(json!({
7816                    "anotherInitValue": false
7817                })),
7818            },
7819        );
7820    });
7821    cx.executor().run_until_parked();
7822    assert_eq!(
7823        server_restarts.load(atomic::Ordering::Acquire),
7824        1,
7825        "Should not restart LSP server on a related LSP settings change that is the same"
7826    );
7827
7828    update_test_project_settings(cx, |project_settings| {
7829        project_settings.lsp.insert(
7830            language_server_name.into(),
7831            LspSettings {
7832                initialization_options: None,
7833            },
7834        );
7835    });
7836    cx.executor().run_until_parked();
7837    assert_eq!(
7838        server_restarts.load(atomic::Ordering::Acquire),
7839        2,
7840        "Should restart LSP server on another related LSP settings change"
7841    );
7842}
7843
7844#[gpui::test]
7845async fn test_completions_with_additional_edits(cx: &mut gpui::TestAppContext) {
7846    init_test(cx, |_| {});
7847
7848    let mut cx = EditorLspTestContext::new_rust(
7849        lsp::ServerCapabilities {
7850            completion_provider: Some(lsp::CompletionOptions {
7851                trigger_characters: Some(vec![".".to_string()]),
7852                resolve_provider: Some(true),
7853                ..Default::default()
7854            }),
7855            ..Default::default()
7856        },
7857        cx,
7858    )
7859    .await;
7860
7861    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
7862    cx.simulate_keystroke(".");
7863    let completion_item = lsp::CompletionItem {
7864        label: "some".into(),
7865        kind: Some(lsp::CompletionItemKind::SNIPPET),
7866        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
7867        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
7868            kind: lsp::MarkupKind::Markdown,
7869            value: "```rust\nSome(2)\n```".to_string(),
7870        })),
7871        deprecated: Some(false),
7872        sort_text: Some("fffffff2".to_string()),
7873        filter_text: Some("some".to_string()),
7874        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
7875        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
7876            range: lsp::Range {
7877                start: lsp::Position {
7878                    line: 0,
7879                    character: 22,
7880                },
7881                end: lsp::Position {
7882                    line: 0,
7883                    character: 22,
7884                },
7885            },
7886            new_text: "Some(2)".to_string(),
7887        })),
7888        additional_text_edits: Some(vec![lsp::TextEdit {
7889            range: lsp::Range {
7890                start: lsp::Position {
7891                    line: 0,
7892                    character: 20,
7893                },
7894                end: lsp::Position {
7895                    line: 0,
7896                    character: 22,
7897                },
7898            },
7899            new_text: "".to_string(),
7900        }]),
7901        ..Default::default()
7902    };
7903
7904    let closure_completion_item = completion_item.clone();
7905    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
7906        let task_completion_item = closure_completion_item.clone();
7907        async move {
7908            Ok(Some(lsp::CompletionResponse::Array(vec![
7909                task_completion_item,
7910            ])))
7911        }
7912    });
7913
7914    request.next().await;
7915
7916    cx.condition(|editor, _| editor.context_menu_visible())
7917        .await;
7918    let apply_additional_edits = cx.update_editor(|editor, cx| {
7919        editor
7920            .confirm_completion(&ConfirmCompletion::default(), cx)
7921            .unwrap()
7922    });
7923    cx.assert_editor_state(indoc! {"fn main() { let a = 2.Some(2)ˇ; }"});
7924
7925    cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
7926        let task_completion_item = completion_item.clone();
7927        async move { Ok(task_completion_item) }
7928    })
7929    .next()
7930    .await
7931    .unwrap();
7932    apply_additional_edits.await.unwrap();
7933    cx.assert_editor_state(indoc! {"fn main() { let a = Some(2)ˇ; }"});
7934}
7935
7936#[gpui::test]
7937async fn test_completions_in_languages_with_extra_word_characters(cx: &mut gpui::TestAppContext) {
7938    init_test(cx, |_| {});
7939
7940    let mut cx = EditorLspTestContext::new(
7941        Language::new(
7942            LanguageConfig {
7943                path_suffixes: vec!["jsx".into()],
7944                overrides: [(
7945                    "element".into(),
7946                    LanguageConfigOverride {
7947                        word_characters: Override::Set(['-'].into_iter().collect()),
7948                        ..Default::default()
7949                    },
7950                )]
7951                .into_iter()
7952                .collect(),
7953                ..Default::default()
7954            },
7955            Some(tree_sitter_typescript::language_tsx()),
7956        )
7957        .with_override_query("(jsx_self_closing_element) @element")
7958        .unwrap(),
7959        lsp::ServerCapabilities {
7960            completion_provider: Some(lsp::CompletionOptions {
7961                trigger_characters: Some(vec![":".to_string()]),
7962                ..Default::default()
7963            }),
7964            ..Default::default()
7965        },
7966        cx,
7967    )
7968    .await;
7969
7970    cx.lsp
7971        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
7972            Ok(Some(lsp::CompletionResponse::Array(vec![
7973                lsp::CompletionItem {
7974                    label: "bg-blue".into(),
7975                    ..Default::default()
7976                },
7977                lsp::CompletionItem {
7978                    label: "bg-red".into(),
7979                    ..Default::default()
7980                },
7981                lsp::CompletionItem {
7982                    label: "bg-yellow".into(),
7983                    ..Default::default()
7984                },
7985            ])))
7986        });
7987
7988    cx.set_state(r#"<p class="bgˇ" />"#);
7989
7990    // Trigger completion when typing a dash, because the dash is an extra
7991    // word character in the 'element' scope, which contains the cursor.
7992    cx.simulate_keystroke("-");
7993    cx.executor().run_until_parked();
7994    cx.update_editor(|editor, _| {
7995        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
7996            assert_eq!(
7997                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
7998                &["bg-red", "bg-blue", "bg-yellow"]
7999            );
8000        } else {
8001            panic!("expected completion menu to be open");
8002        }
8003    });
8004
8005    cx.simulate_keystroke("l");
8006    cx.executor().run_until_parked();
8007    cx.update_editor(|editor, _| {
8008        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
8009            assert_eq!(
8010                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
8011                &["bg-blue", "bg-yellow"]
8012            );
8013        } else {
8014            panic!("expected completion menu to be open");
8015        }
8016    });
8017
8018    // When filtering completions, consider the character after the '-' to
8019    // be the start of a subword.
8020    cx.set_state(r#"<p class="yelˇ" />"#);
8021    cx.simulate_keystroke("l");
8022    cx.executor().run_until_parked();
8023    cx.update_editor(|editor, _| {
8024        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
8025            assert_eq!(
8026                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
8027                &["bg-yellow"]
8028            );
8029        } else {
8030            panic!("expected completion menu to be open");
8031        }
8032    });
8033}
8034
8035#[gpui::test]
8036async fn test_document_format_with_prettier(cx: &mut gpui::TestAppContext) {
8037    init_test(cx, |settings| {
8038        settings.defaults.formatter = Some(language_settings::Formatter::Prettier)
8039    });
8040
8041    let mut language = Language::new(
8042        LanguageConfig {
8043            name: "Rust".into(),
8044            path_suffixes: vec!["rs".to_string()],
8045            prettier_parser_name: Some("test_parser".to_string()),
8046            ..Default::default()
8047        },
8048        Some(tree_sitter_rust::language()),
8049    );
8050
8051    let test_plugin = "test_plugin";
8052    let _ = language
8053        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
8054            prettier_plugins: vec![test_plugin],
8055            ..Default::default()
8056        }))
8057        .await;
8058
8059    let fs = FakeFs::new(cx.executor());
8060    fs.insert_file("/file.rs", Default::default()).await;
8061
8062    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
8063    let prettier_format_suffix = project::TEST_PRETTIER_FORMAT_SUFFIX;
8064    _ = project.update(cx, |project, _| {
8065        project.languages().add(Arc::new(language));
8066    });
8067    let buffer = project
8068        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
8069        .await
8070        .unwrap();
8071
8072    let buffer_text = "one\ntwo\nthree\n";
8073    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
8074    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
8075    _ = editor.update(cx, |editor, cx| editor.set_text(buffer_text, cx));
8076
8077    editor
8078        .update(cx, |editor, cx| {
8079            editor.perform_format(project.clone(), FormatTrigger::Manual, cx)
8080        })
8081        .unwrap()
8082        .await;
8083    assert_eq!(
8084        editor.update(cx, |editor, cx| editor.text(cx)),
8085        buffer_text.to_string() + prettier_format_suffix,
8086        "Test prettier formatting was not applied to the original buffer text",
8087    );
8088
8089    update_test_language_settings(cx, |settings| {
8090        settings.defaults.formatter = Some(language_settings::Formatter::Auto)
8091    });
8092    let format = editor.update(cx, |editor, cx| {
8093        editor.perform_format(project.clone(), FormatTrigger::Manual, cx)
8094    });
8095    format.await.unwrap();
8096    assert_eq!(
8097        editor.update(cx, |editor, cx| editor.text(cx)),
8098        buffer_text.to_string() + prettier_format_suffix + "\n" + prettier_format_suffix,
8099        "Autoformatting (via test prettier) was not applied to the original buffer text",
8100    );
8101}
8102
8103fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
8104    let point = DisplayPoint::new(row as u32, column as u32);
8105    point..point
8106}
8107
8108fn assert_selection_ranges(marked_text: &str, view: &mut Editor, cx: &mut ViewContext<Editor>) {
8109    let (text, ranges) = marked_text_ranges(marked_text, true);
8110    assert_eq!(view.text(cx), text);
8111    assert_eq!(
8112        view.selections.ranges(cx),
8113        ranges,
8114        "Assert selections are {}",
8115        marked_text
8116    );
8117}
8118
8119/// Handle completion request passing a marked string specifying where the completion
8120/// should be triggered from using '|' character, what range should be replaced, and what completions
8121/// should be returned using '<' and '>' to delimit the range
8122pub fn handle_completion_request(
8123    cx: &mut EditorLspTestContext,
8124    marked_string: &str,
8125    completions: Vec<&'static str>,
8126) -> impl Future<Output = ()> {
8127    let complete_from_marker: TextRangeMarker = '|'.into();
8128    let replace_range_marker: TextRangeMarker = ('<', '>').into();
8129    let (_, mut marked_ranges) = marked_text_ranges_by(
8130        marked_string,
8131        vec![complete_from_marker.clone(), replace_range_marker.clone()],
8132    );
8133
8134    let complete_from_position =
8135        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
8136    let replace_range =
8137        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
8138
8139    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |url, params, _| {
8140        let completions = completions.clone();
8141        async move {
8142            assert_eq!(params.text_document_position.text_document.uri, url.clone());
8143            assert_eq!(
8144                params.text_document_position.position,
8145                complete_from_position
8146            );
8147            Ok(Some(lsp::CompletionResponse::Array(
8148                completions
8149                    .iter()
8150                    .map(|completion_text| lsp::CompletionItem {
8151                        label: completion_text.to_string(),
8152                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
8153                            range: replace_range,
8154                            new_text: completion_text.to_string(),
8155                        })),
8156                        ..Default::default()
8157                    })
8158                    .collect(),
8159            )))
8160        }
8161    });
8162
8163    async move {
8164        request.next().await;
8165    }
8166}
8167
8168fn handle_resolve_completion_request(
8169    cx: &mut EditorLspTestContext,
8170    edits: Option<Vec<(&'static str, &'static str)>>,
8171) -> impl Future<Output = ()> {
8172    let edits = edits.map(|edits| {
8173        edits
8174            .iter()
8175            .map(|(marked_string, new_text)| {
8176                let (_, marked_ranges) = marked_text_ranges(marked_string, false);
8177                let replace_range = cx.to_lsp_range(marked_ranges[0].clone());
8178                lsp::TextEdit::new(replace_range, new_text.to_string())
8179            })
8180            .collect::<Vec<_>>()
8181    });
8182
8183    let mut request =
8184        cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
8185            let edits = edits.clone();
8186            async move {
8187                Ok(lsp::CompletionItem {
8188                    additional_text_edits: edits,
8189                    ..Default::default()
8190                })
8191            }
8192        });
8193
8194    async move {
8195        request.next().await;
8196    }
8197}
8198
8199fn handle_copilot_completion_request(
8200    lsp: &lsp::FakeLanguageServer,
8201    completions: Vec<copilot::request::Completion>,
8202    completions_cycling: Vec<copilot::request::Completion>,
8203) {
8204    lsp.handle_request::<copilot::request::GetCompletions, _, _>(move |_params, _cx| {
8205        let completions = completions.clone();
8206        async move {
8207            Ok(copilot::request::GetCompletionsResult {
8208                completions: completions.clone(),
8209            })
8210        }
8211    });
8212    lsp.handle_request::<copilot::request::GetCompletionsCycling, _, _>(move |_params, _cx| {
8213        let completions_cycling = completions_cycling.clone();
8214        async move {
8215            Ok(copilot::request::GetCompletionsResult {
8216                completions: completions_cycling.clone(),
8217            })
8218        }
8219    });
8220}
8221
8222pub(crate) fn update_test_language_settings(
8223    cx: &mut TestAppContext,
8224    f: impl Fn(&mut AllLanguageSettingsContent),
8225) {
8226    _ = cx.update(|cx| {
8227        cx.update_global(|store: &mut SettingsStore, cx| {
8228            store.update_user_settings::<AllLanguageSettings>(cx, f);
8229        });
8230    });
8231}
8232
8233pub(crate) fn update_test_project_settings(
8234    cx: &mut TestAppContext,
8235    f: impl Fn(&mut ProjectSettings),
8236) {
8237    _ = cx.update(|cx| {
8238        cx.update_global(|store: &mut SettingsStore, cx| {
8239            store.update_user_settings::<ProjectSettings>(cx, f);
8240        });
8241    });
8242}
8243
8244pub(crate) fn init_test(cx: &mut TestAppContext, f: fn(&mut AllLanguageSettingsContent)) {
8245    _ = cx.update(|cx| {
8246        let store = SettingsStore::test(cx);
8247        cx.set_global(store);
8248        theme::init(theme::LoadThemes::JustBase, cx);
8249        client::init_settings(cx);
8250        language::init(cx);
8251        Project::init_settings(cx);
8252        workspace::init_settings(cx);
8253        crate::init(cx);
8254    });
8255
8256    update_test_language_settings(cx, f);
8257}