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