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