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