editor_tests.rs

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