editor_tests.rs

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