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