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, 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]
 962// fn 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]
1228// fn 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]
2496// fn 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]
2596// fn 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]
3241// async 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
3277//             )ˇ
3278//         );
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
3293//         )ˇ
3294//         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,
3334//                 fˇ
3335//             )
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]
6411// async 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: &Event, cx| {
6458//                     if matches!(event.to_follow_event(), Some(FollowEvent::Unfollow)) {
6459//                         *is_still_following.borrow_mut() = false;
6460//                     }
6461
6462//                     if let Event::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]
6575// async 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(|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//         .await
6609//         .unwrap();
6610
6611//     let update_message = Rc::new(RefCell::new(None));
6612//     follower_1.update(cx, {
6613//         let update = update_message.clone();
6614//         |_, cx| {
6615//             cx.subscribe(&leader, move |_, leader, event, cx| {
6616//                 leader
6617//                     .read(cx)
6618//                     .add_event_to_update_proto(event, &mut *update.borrow_mut(), cx);
6619//             })
6620//             .detach();
6621//         }
6622//     });
6623
6624//     let (buffer_1, buffer_2) = project.update(cx, |project, cx| {
6625//         (
6626//             project
6627//                 .create_buffer("abc\ndef\nghi\njkl\n", None, cx)
6628//                 .unwrap(),
6629//             project
6630//                 .create_buffer("mno\npqr\nstu\nvwx\n", None, cx)
6631//                 .unwrap(),
6632//         )
6633//     });
6634
6635//     // Insert some excerpts.
6636//     leader.update(cx, |leader, cx| {
6637//         leader.buffer.update(cx, |multibuffer, cx| {
6638//             let excerpt_ids = multibuffer.push_excerpts(
6639//                 buffer_1.clone(),
6640//                 [
6641//                     ExcerptRange {
6642//                         context: 1..6,
6643//                         primary: None,
6644//                     },
6645//                     ExcerptRange {
6646//                         context: 12..15,
6647//                         primary: None,
6648//                     },
6649//                     ExcerptRange {
6650//                         context: 0..3,
6651//                         primary: None,
6652//                     },
6653//                 ],
6654//                 cx,
6655//             );
6656//             multibuffer.insert_excerpts_after(
6657//                 excerpt_ids[0],
6658//                 buffer_2.clone(),
6659//                 [
6660//                     ExcerptRange {
6661//                         context: 8..12,
6662//                         primary: None,
6663//                     },
6664//                     ExcerptRange {
6665//                         context: 0..6,
6666//                         primary: None,
6667//                     },
6668//                 ],
6669//                 cx,
6670//             );
6671//         });
6672//     });
6673
6674//     // Apply the update of adding the excerpts.
6675//     follower_1
6676//         .update(cx, |follower, cx| {
6677//             follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
6678//         })
6679//         .await
6680//         .unwrap();
6681//     assert_eq!(
6682//         follower_1.update(cx, |editor, cx| editor.text(cx)),
6683//         leader.update(cx, |editor, cx| editor.text(cx))
6684//     );
6685//     update_message.borrow_mut().take();
6686
6687//     // Start following separately after it already has excerpts.
6688//     let mut state_message = leader.update(cx, |leader, cx| leader.to_state_proto(cx));
6689//     let follower_2 = cx
6690//         .update(|cx| {
6691//             Editor::from_state_proto(
6692//                 pane.clone(),
6693//                 workspace.clone(),
6694//                 ViewId {
6695//                     creator: Default::default(),
6696//                     id: 0,
6697//                 },
6698//                 &mut state_message,
6699//                 cx,
6700//             )
6701//         })
6702//         .unwrap()
6703//         .await
6704//         .unwrap();
6705//     assert_eq!(
6706//         follower_2.update(cx, |editor, cx| editor.text(cx)),
6707//         leader.update(cx, |editor, cx| editor.text(cx))
6708//     );
6709
6710//     // Remove some excerpts.
6711//     leader.update(cx, |leader, cx| {
6712//         leader.buffer.update(cx, |multibuffer, cx| {
6713//             let excerpt_ids = multibuffer.excerpt_ids();
6714//             multibuffer.remove_excerpts([excerpt_ids[1], excerpt_ids[2]], cx);
6715//             multibuffer.remove_excerpts([excerpt_ids[0]], cx);
6716//         });
6717//     });
6718
6719//     // Apply the update of removing the excerpts.
6720//     follower_1
6721//         .update(cx, |follower, cx| {
6722//             follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
6723//         })
6724//         .await
6725//         .unwrap();
6726//     follower_2
6727//         .update(cx, |follower, cx| {
6728//             follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
6729//         })
6730//         .await
6731//         .unwrap();
6732//     update_message.borrow_mut().take();
6733//     assert_eq!(
6734//         follower_1.update(cx, |editor, cx| editor.text(cx)),
6735//         leader.update(cx, |editor, cx| editor.text(cx))
6736//     );
6737// }
6738
6739#[gpui::test]
6740async fn go_to_prev_overlapping_diagnostic(
6741    executor: BackgroundExecutor,
6742    cx: &mut gpui::TestAppContext,
6743) {
6744    init_test(cx, |_| {});
6745
6746    let mut cx = EditorTestContext::new(cx).await;
6747    let project = cx.update_editor(|editor, _| editor.project.clone().unwrap());
6748
6749    cx.set_state(indoc! {"
6750        ˇfn func(abc def: i32) -> u32 {
6751        }
6752    "});
6753
6754    cx.update(|cx| {
6755        project.update(cx, |project, cx| {
6756            project
6757                .update_diagnostics(
6758                    LanguageServerId(0),
6759                    lsp::PublishDiagnosticsParams {
6760                        uri: lsp::Url::from_file_path("/root/file").unwrap(),
6761                        version: None,
6762                        diagnostics: vec![
6763                            lsp::Diagnostic {
6764                                range: lsp::Range::new(
6765                                    lsp::Position::new(0, 11),
6766                                    lsp::Position::new(0, 12),
6767                                ),
6768                                severity: Some(lsp::DiagnosticSeverity::ERROR),
6769                                ..Default::default()
6770                            },
6771                            lsp::Diagnostic {
6772                                range: lsp::Range::new(
6773                                    lsp::Position::new(0, 12),
6774                                    lsp::Position::new(0, 15),
6775                                ),
6776                                severity: Some(lsp::DiagnosticSeverity::ERROR),
6777                                ..Default::default()
6778                            },
6779                            lsp::Diagnostic {
6780                                range: lsp::Range::new(
6781                                    lsp::Position::new(0, 25),
6782                                    lsp::Position::new(0, 28),
6783                                ),
6784                                severity: Some(lsp::DiagnosticSeverity::ERROR),
6785                                ..Default::default()
6786                            },
6787                        ],
6788                    },
6789                    &[],
6790                    cx,
6791                )
6792                .unwrap()
6793        });
6794    });
6795
6796    executor.run_until_parked();
6797
6798    cx.update_editor(|editor, cx| {
6799        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
6800    });
6801
6802    cx.assert_editor_state(indoc! {"
6803        fn func(abc def: i32) -> ˇu32 {
6804        }
6805    "});
6806
6807    cx.update_editor(|editor, cx| {
6808        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
6809    });
6810
6811    cx.assert_editor_state(indoc! {"
6812        fn func(abc ˇdef: i32) -> u32 {
6813        }
6814    "});
6815
6816    cx.update_editor(|editor, cx| {
6817        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
6818    });
6819
6820    cx.assert_editor_state(indoc! {"
6821        fn func(abcˇ def: i32) -> u32 {
6822        }
6823    "});
6824
6825    cx.update_editor(|editor, cx| {
6826        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
6827    });
6828
6829    cx.assert_editor_state(indoc! {"
6830        fn func(abc def: i32) -> ˇu32 {
6831        }
6832    "});
6833}
6834
6835#[gpui::test]
6836async fn go_to_hunk(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
6837    init_test(cx, |_| {});
6838
6839    let mut cx = EditorTestContext::new(cx).await;
6840
6841    let diff_base = r#"
6842        use some::mod;
6843
6844        const A: u32 = 42;
6845
6846        fn main() {
6847            println!("hello");
6848
6849            println!("world");
6850        }
6851        "#
6852    .unindent();
6853
6854    // Edits are modified, removed, modified, added
6855    cx.set_state(
6856        &r#"
6857        use some::modified;
6858
6859        ˇ
6860        fn main() {
6861            println!("hello there");
6862
6863            println!("around the");
6864            println!("world");
6865        }
6866        "#
6867        .unindent(),
6868    );
6869
6870    cx.set_diff_base(Some(&diff_base));
6871    executor.run_until_parked();
6872
6873    cx.update_editor(|editor, cx| {
6874        //Wrap around the bottom of the buffer
6875        for _ in 0..3 {
6876            editor.go_to_hunk(&GoToHunk, cx);
6877        }
6878    });
6879
6880    cx.assert_editor_state(
6881        &r#"
6882        ˇuse some::modified;
6883
6884
6885        fn main() {
6886            println!("hello there");
6887
6888            println!("around the");
6889            println!("world");
6890        }
6891        "#
6892        .unindent(),
6893    );
6894
6895    cx.update_editor(|editor, cx| {
6896        //Wrap around the top of the buffer
6897        for _ in 0..2 {
6898            editor.go_to_prev_hunk(&GoToPrevHunk, cx);
6899        }
6900    });
6901
6902    cx.assert_editor_state(
6903        &r#"
6904        use some::modified;
6905
6906
6907        fn main() {
6908        ˇ    println!("hello there");
6909
6910            println!("around the");
6911            println!("world");
6912        }
6913        "#
6914        .unindent(),
6915    );
6916
6917    cx.update_editor(|editor, cx| {
6918        editor.go_to_prev_hunk(&GoToPrevHunk, cx);
6919    });
6920
6921    cx.assert_editor_state(
6922        &r#"
6923        use some::modified;
6924
6925        ˇ
6926        fn main() {
6927            println!("hello there");
6928
6929            println!("around the");
6930            println!("world");
6931        }
6932        "#
6933        .unindent(),
6934    );
6935
6936    cx.update_editor(|editor, cx| {
6937        for _ in 0..3 {
6938            editor.go_to_prev_hunk(&GoToPrevHunk, cx);
6939        }
6940    });
6941
6942    cx.assert_editor_state(
6943        &r#"
6944        use some::modified;
6945
6946
6947        fn main() {
6948        ˇ    println!("hello there");
6949
6950            println!("around the");
6951            println!("world");
6952        }
6953        "#
6954        .unindent(),
6955    );
6956
6957    cx.update_editor(|editor, cx| {
6958        editor.fold(&Fold, cx);
6959
6960        //Make sure that the fold only gets one hunk
6961        for _ in 0..4 {
6962            editor.go_to_hunk(&GoToHunk, cx);
6963        }
6964    });
6965
6966    cx.assert_editor_state(
6967        &r#"
6968        ˇuse some::modified;
6969
6970
6971        fn main() {
6972            println!("hello there");
6973
6974            println!("around the");
6975            println!("world");
6976        }
6977        "#
6978        .unindent(),
6979    );
6980}
6981
6982#[test]
6983fn test_split_words() {
6984    fn split<'a>(text: &'a str) -> Vec<&'a str> {
6985        split_words(text).collect()
6986    }
6987
6988    assert_eq!(split("HelloWorld"), &["Hello", "World"]);
6989    assert_eq!(split("hello_world"), &["hello_", "world"]);
6990    assert_eq!(split("_hello_world_"), &["_", "hello_", "world_"]);
6991    assert_eq!(split("Hello_World"), &["Hello_", "World"]);
6992    assert_eq!(split("helloWOrld"), &["hello", "WOrld"]);
6993    assert_eq!(split("helloworld"), &["helloworld"]);
6994}
6995
6996#[gpui::test]
6997async fn test_move_to_enclosing_bracket(cx: &mut gpui::TestAppContext) {
6998    init_test(cx, |_| {});
6999
7000    let mut cx = EditorLspTestContext::new_typescript(Default::default(), cx).await;
7001    let mut assert = |before, after| {
7002        let _state_context = cx.set_state(before);
7003        cx.update_editor(|editor, cx| {
7004            editor.move_to_enclosing_bracket(&MoveToEnclosingBracket, cx)
7005        });
7006        cx.assert_editor_state(after);
7007    };
7008
7009    // Outside bracket jumps to outside of matching bracket
7010    assert("console.logˇ(var);", "console.log(var)ˇ;");
7011    assert("console.log(var)ˇ;", "console.logˇ(var);");
7012
7013    // Inside bracket jumps to inside of matching bracket
7014    assert("console.log(ˇvar);", "console.log(varˇ);");
7015    assert("console.log(varˇ);", "console.log(ˇvar);");
7016
7017    // When outside a bracket and inside, favor jumping to the inside bracket
7018    assert(
7019        "console.log('foo', [1, 2, 3]ˇ);",
7020        "console.log(ˇ'foo', [1, 2, 3]);",
7021    );
7022    assert(
7023        "console.log(ˇ'foo', [1, 2, 3]);",
7024        "console.log('foo', [1, 2, 3]ˇ);",
7025    );
7026
7027    // Bias forward if two options are equally likely
7028    assert(
7029        "let result = curried_fun()ˇ();",
7030        "let result = curried_fun()()ˇ;",
7031    );
7032
7033    // If directly adjacent to a smaller pair but inside a larger (not adjacent), pick the smaller
7034    assert(
7035        indoc! {"
7036            function test() {
7037                console.log('test')ˇ
7038            }"},
7039        indoc! {"
7040            function test() {
7041                console.logˇ('test')
7042            }"},
7043    );
7044}
7045
7046// todo!(completions)
7047#[gpui::test(iterations = 10)]
7048async fn test_copilot(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
7049    // flaky
7050    init_test(cx, |_| {});
7051
7052    let (copilot, copilot_lsp) = Copilot::fake(cx);
7053    cx.update(|cx| cx.set_global(copilot));
7054    let mut cx = EditorLspTestContext::new_rust(
7055        lsp::ServerCapabilities {
7056            completion_provider: Some(lsp::CompletionOptions {
7057                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
7058                ..Default::default()
7059            }),
7060            ..Default::default()
7061        },
7062        cx,
7063    )
7064    .await;
7065
7066    // When inserting, ensure autocompletion is favored over Copilot suggestions.
7067    cx.set_state(indoc! {"
7068        oneˇ
7069        two
7070        three
7071    "});
7072    cx.simulate_keystroke(".");
7073    let _ = handle_completion_request(
7074        &mut cx,
7075        indoc! {"
7076            one.|<>
7077            two
7078            three
7079        "},
7080        vec!["completion_a", "completion_b"],
7081    );
7082    handle_copilot_completion_request(
7083        &copilot_lsp,
7084        vec![copilot::request::Completion {
7085            text: "one.copilot1".into(),
7086            range: lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 4)),
7087            ..Default::default()
7088        }],
7089        vec![],
7090    );
7091    executor.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
7092    cx.update_editor(|editor, cx| {
7093        assert!(editor.context_menu_visible());
7094        assert!(!editor.has_active_copilot_suggestion(cx));
7095
7096        // Confirming a completion inserts it and hides the context menu, without showing
7097        // the copilot suggestion afterwards.
7098        editor
7099            .confirm_completion(&Default::default(), cx)
7100            .unwrap()
7101            .detach();
7102        assert!(!editor.context_menu_visible());
7103        assert!(!editor.has_active_copilot_suggestion(cx));
7104        assert_eq!(editor.text(cx), "one.completion_a\ntwo\nthree\n");
7105        assert_eq!(editor.display_text(cx), "one.completion_a\ntwo\nthree\n");
7106    });
7107
7108    // Ensure Copilot suggestions are shown right away if no autocompletion is available.
7109    cx.set_state(indoc! {"
7110        oneˇ
7111        two
7112        three
7113    "});
7114    cx.simulate_keystroke(".");
7115    let _ = handle_completion_request(
7116        &mut cx,
7117        indoc! {"
7118            one.|<>
7119            two
7120            three
7121        "},
7122        vec![],
7123    );
7124    handle_copilot_completion_request(
7125        &copilot_lsp,
7126        vec![copilot::request::Completion {
7127            text: "one.copilot1".into(),
7128            range: lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 4)),
7129            ..Default::default()
7130        }],
7131        vec![],
7132    );
7133    executor.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
7134    cx.update_editor(|editor, cx| {
7135        assert!(!editor.context_menu_visible());
7136        assert!(editor.has_active_copilot_suggestion(cx));
7137        assert_eq!(editor.display_text(cx), "one.copilot1\ntwo\nthree\n");
7138        assert_eq!(editor.text(cx), "one.\ntwo\nthree\n");
7139    });
7140
7141    // Reset editor, and ensure autocompletion is still favored over Copilot suggestions.
7142    cx.set_state(indoc! {"
7143        oneˇ
7144        two
7145        three
7146    "});
7147    cx.simulate_keystroke(".");
7148    let _ = handle_completion_request(
7149        &mut cx,
7150        indoc! {"
7151            one.|<>
7152            two
7153            three
7154        "},
7155        vec!["completion_a", "completion_b"],
7156    );
7157    handle_copilot_completion_request(
7158        &copilot_lsp,
7159        vec![copilot::request::Completion {
7160            text: "one.copilot1".into(),
7161            range: lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 4)),
7162            ..Default::default()
7163        }],
7164        vec![],
7165    );
7166    executor.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
7167    cx.update_editor(|editor, cx| {
7168        assert!(editor.context_menu_visible());
7169        assert!(!editor.has_active_copilot_suggestion(cx));
7170
7171        // When hiding the context menu, the Copilot suggestion becomes visible.
7172        editor.hide_context_menu(cx);
7173        assert!(!editor.context_menu_visible());
7174        assert!(editor.has_active_copilot_suggestion(cx));
7175        assert_eq!(editor.display_text(cx), "one.copilot1\ntwo\nthree\n");
7176        assert_eq!(editor.text(cx), "one.\ntwo\nthree\n");
7177    });
7178
7179    // Ensure existing completion is interpolated when inserting again.
7180    cx.simulate_keystroke("c");
7181    executor.run_until_parked();
7182    cx.update_editor(|editor, cx| {
7183        assert!(!editor.context_menu_visible());
7184        assert!(editor.has_active_copilot_suggestion(cx));
7185        assert_eq!(editor.display_text(cx), "one.copilot1\ntwo\nthree\n");
7186        assert_eq!(editor.text(cx), "one.c\ntwo\nthree\n");
7187    });
7188
7189    // After debouncing, new Copilot completions should be requested.
7190    handle_copilot_completion_request(
7191        &copilot_lsp,
7192        vec![copilot::request::Completion {
7193            text: "one.copilot2".into(),
7194            range: lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 5)),
7195            ..Default::default()
7196        }],
7197        vec![],
7198    );
7199    executor.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
7200    cx.update_editor(|editor, cx| {
7201        assert!(!editor.context_menu_visible());
7202        assert!(editor.has_active_copilot_suggestion(cx));
7203        assert_eq!(editor.display_text(cx), "one.copilot2\ntwo\nthree\n");
7204        assert_eq!(editor.text(cx), "one.c\ntwo\nthree\n");
7205
7206        // Canceling should remove the active Copilot suggestion.
7207        editor.cancel(&Default::default(), cx);
7208        assert!(!editor.has_active_copilot_suggestion(cx));
7209        assert_eq!(editor.display_text(cx), "one.c\ntwo\nthree\n");
7210        assert_eq!(editor.text(cx), "one.c\ntwo\nthree\n");
7211
7212        // After canceling, tabbing shouldn't insert the previously shown suggestion.
7213        editor.tab(&Default::default(), cx);
7214        assert!(!editor.has_active_copilot_suggestion(cx));
7215        assert_eq!(editor.display_text(cx), "one.c   \ntwo\nthree\n");
7216        assert_eq!(editor.text(cx), "one.c   \ntwo\nthree\n");
7217
7218        // When undoing the previously active suggestion is shown again.
7219        editor.undo(&Default::default(), cx);
7220        assert!(editor.has_active_copilot_suggestion(cx));
7221        assert_eq!(editor.display_text(cx), "one.copilot2\ntwo\nthree\n");
7222        assert_eq!(editor.text(cx), "one.c\ntwo\nthree\n");
7223    });
7224
7225    // If an edit occurs outside of this editor, the suggestion is still correctly interpolated.
7226    cx.update_buffer(|buffer, cx| buffer.edit([(5..5, "o")], None, cx));
7227    cx.update_editor(|editor, cx| {
7228        assert!(editor.has_active_copilot_suggestion(cx));
7229        assert_eq!(editor.display_text(cx), "one.copilot2\ntwo\nthree\n");
7230        assert_eq!(editor.text(cx), "one.co\ntwo\nthree\n");
7231
7232        // Tabbing when there is an active suggestion inserts it.
7233        editor.tab(&Default::default(), cx);
7234        assert!(!editor.has_active_copilot_suggestion(cx));
7235        assert_eq!(editor.display_text(cx), "one.copilot2\ntwo\nthree\n");
7236        assert_eq!(editor.text(cx), "one.copilot2\ntwo\nthree\n");
7237
7238        // When undoing the previously active suggestion is shown again.
7239        editor.undo(&Default::default(), cx);
7240        assert!(editor.has_active_copilot_suggestion(cx));
7241        assert_eq!(editor.display_text(cx), "one.copilot2\ntwo\nthree\n");
7242        assert_eq!(editor.text(cx), "one.co\ntwo\nthree\n");
7243
7244        // Hide suggestion.
7245        editor.cancel(&Default::default(), cx);
7246        assert!(!editor.has_active_copilot_suggestion(cx));
7247        assert_eq!(editor.display_text(cx), "one.co\ntwo\nthree\n");
7248        assert_eq!(editor.text(cx), "one.co\ntwo\nthree\n");
7249    });
7250
7251    // If an edit occurs outside of this editor but no suggestion is being shown,
7252    // we won't make it visible.
7253    cx.update_buffer(|buffer, cx| buffer.edit([(6..6, "p")], None, cx));
7254    cx.update_editor(|editor, cx| {
7255        assert!(!editor.has_active_copilot_suggestion(cx));
7256        assert_eq!(editor.display_text(cx), "one.cop\ntwo\nthree\n");
7257        assert_eq!(editor.text(cx), "one.cop\ntwo\nthree\n");
7258    });
7259
7260    // Reset the editor to verify how suggestions behave when tabbing on leading indentation.
7261    cx.update_editor(|editor, cx| {
7262        editor.set_text("fn foo() {\n  \n}", cx);
7263        editor.change_selections(None, cx, |s| {
7264            s.select_ranges([Point::new(1, 2)..Point::new(1, 2)])
7265        });
7266    });
7267    handle_copilot_completion_request(
7268        &copilot_lsp,
7269        vec![copilot::request::Completion {
7270            text: "    let x = 4;".into(),
7271            range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 2)),
7272            ..Default::default()
7273        }],
7274        vec![],
7275    );
7276
7277    cx.update_editor(|editor, cx| editor.next_copilot_suggestion(&Default::default(), cx));
7278    executor.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
7279    cx.update_editor(|editor, cx| {
7280        assert!(editor.has_active_copilot_suggestion(cx));
7281        assert_eq!(editor.display_text(cx), "fn foo() {\n    let x = 4;\n}");
7282        assert_eq!(editor.text(cx), "fn foo() {\n  \n}");
7283
7284        // Tabbing inside of leading whitespace inserts indentation without accepting the suggestion.
7285        editor.tab(&Default::default(), cx);
7286        assert!(editor.has_active_copilot_suggestion(cx));
7287        assert_eq!(editor.text(cx), "fn foo() {\n    \n}");
7288        assert_eq!(editor.display_text(cx), "fn foo() {\n    let x = 4;\n}");
7289
7290        // Tabbing again accepts the suggestion.
7291        editor.tab(&Default::default(), cx);
7292        assert!(!editor.has_active_copilot_suggestion(cx));
7293        assert_eq!(editor.text(cx), "fn foo() {\n    let x = 4;\n}");
7294        assert_eq!(editor.display_text(cx), "fn foo() {\n    let x = 4;\n}");
7295    });
7296}
7297
7298#[gpui::test]
7299async fn test_copilot_completion_invalidation(
7300    executor: BackgroundExecutor,
7301    cx: &mut gpui::TestAppContext,
7302) {
7303    init_test(cx, |_| {});
7304
7305    let (copilot, copilot_lsp) = Copilot::fake(cx);
7306    cx.update(|cx| cx.set_global(copilot));
7307    let mut cx = EditorLspTestContext::new_rust(
7308        lsp::ServerCapabilities {
7309            completion_provider: Some(lsp::CompletionOptions {
7310                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
7311                ..Default::default()
7312            }),
7313            ..Default::default()
7314        },
7315        cx,
7316    )
7317    .await;
7318
7319    cx.set_state(indoc! {"
7320        one
7321        twˇ
7322        three
7323    "});
7324
7325    handle_copilot_completion_request(
7326        &copilot_lsp,
7327        vec![copilot::request::Completion {
7328            text: "two.foo()".into(),
7329            range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 2)),
7330            ..Default::default()
7331        }],
7332        vec![],
7333    );
7334    cx.update_editor(|editor, cx| editor.next_copilot_suggestion(&Default::default(), cx));
7335    executor.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
7336    cx.update_editor(|editor, cx| {
7337        assert!(editor.has_active_copilot_suggestion(cx));
7338        assert_eq!(editor.display_text(cx), "one\ntwo.foo()\nthree\n");
7339        assert_eq!(editor.text(cx), "one\ntw\nthree\n");
7340
7341        editor.backspace(&Default::default(), cx);
7342        assert!(editor.has_active_copilot_suggestion(cx));
7343        assert_eq!(editor.display_text(cx), "one\ntwo.foo()\nthree\n");
7344        assert_eq!(editor.text(cx), "one\nt\nthree\n");
7345
7346        editor.backspace(&Default::default(), cx);
7347        assert!(editor.has_active_copilot_suggestion(cx));
7348        assert_eq!(editor.display_text(cx), "one\ntwo.foo()\nthree\n");
7349        assert_eq!(editor.text(cx), "one\n\nthree\n");
7350
7351        // Deleting across the original suggestion range invalidates it.
7352        editor.backspace(&Default::default(), cx);
7353        assert!(!editor.has_active_copilot_suggestion(cx));
7354        assert_eq!(editor.display_text(cx), "one\nthree\n");
7355        assert_eq!(editor.text(cx), "one\nthree\n");
7356
7357        // Undoing the deletion restores the suggestion.
7358        editor.undo(&Default::default(), cx);
7359        assert!(editor.has_active_copilot_suggestion(cx));
7360        assert_eq!(editor.display_text(cx), "one\ntwo.foo()\nthree\n");
7361        assert_eq!(editor.text(cx), "one\n\nthree\n");
7362    });
7363}
7364
7365//todo!()
7366// #[gpui::test]
7367// async fn test_copilot_multibuffer(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
7368//     init_test(cx, |_| {});
7369
7370//     let (copilot, copilot_lsp) = Copilot::fake(cx);
7371//     cx.update(|cx| cx.set_global(copilot));
7372
7373//     let buffer_1 = cx.build_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), "a = 1\nb = 2\n"));
7374//     let buffer_2 = cx.build_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), "c = 3\nd = 4\n"));
7375//     let multibuffer = cx.build_model(|cx| {
7376//         let mut multibuffer = MultiBuffer::new(0);
7377//         multibuffer.push_excerpts(
7378//             buffer_1.clone(),
7379//             [ExcerptRange {
7380//                 context: Point::new(0, 0)..Point::new(2, 0),
7381//                 primary: None,
7382//             }],
7383//             cx,
7384//         );
7385//         multibuffer.push_excerpts(
7386//             buffer_2.clone(),
7387//             [ExcerptRange {
7388//                 context: Point::new(0, 0)..Point::new(2, 0),
7389//                 primary: None,
7390//             }],
7391//             cx,
7392//         );
7393//         multibuffer
7394//     });
7395//     let editor = cx.add_window(|cx| build_editor(multibuffer, cx));
7396
7397//     handle_copilot_completion_request(
7398//         &copilot_lsp,
7399//         vec![copilot::request::Completion {
7400//             text: "b = 2 + a".into(),
7401//             range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 5)),
7402//             ..Default::default()
7403//         }],
7404//         vec![],
7405//     );
7406//     editor.update(cx, |editor, cx| {
7407//         // Ensure copilot suggestions are shown for the first excerpt.
7408//         editor.change_selections(None, cx, |s| {
7409//             s.select_ranges([Point::new(1, 5)..Point::new(1, 5)])
7410//         });
7411//         editor.next_copilot_suggestion(&Default::default(), cx);
7412//     });
7413//     executor.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
7414//     editor.update(cx, |editor, cx| {
7415//         assert!(editor.has_active_copilot_suggestion(cx));
7416//         assert_eq!(
7417//             editor.display_text(cx),
7418//             "\n\na = 1\nb = 2 + a\n\n\n\nc = 3\nd = 4\n"
7419//         );
7420//         assert_eq!(editor.text(cx), "a = 1\nb = 2\n\nc = 3\nd = 4\n");
7421//     });
7422
7423//     handle_copilot_completion_request(
7424//         &copilot_lsp,
7425//         vec![copilot::request::Completion {
7426//             text: "d = 4 + c".into(),
7427//             range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 6)),
7428//             ..Default::default()
7429//         }],
7430//         vec![],
7431//     );
7432//     editor.update(cx, |editor, cx| {
7433//         // Move to another excerpt, ensuring the suggestion gets cleared.
7434//         editor.change_selections(None, cx, |s| {
7435//             s.select_ranges([Point::new(4, 5)..Point::new(4, 5)])
7436//         });
7437//         assert!(!editor.has_active_copilot_suggestion(cx));
7438//         assert_eq!(
7439//             editor.display_text(cx),
7440//             "\n\na = 1\nb = 2\n\n\n\nc = 3\nd = 4\n"
7441//         );
7442//         assert_eq!(editor.text(cx), "a = 1\nb = 2\n\nc = 3\nd = 4\n");
7443
7444//         // Type a character, ensuring we don't even try to interpolate the previous suggestion.
7445//         editor.handle_input(" ", cx);
7446//         assert!(!editor.has_active_copilot_suggestion(cx));
7447//         assert_eq!(
7448//             editor.display_text(cx),
7449//             "\n\na = 1\nb = 2\n\n\n\nc = 3\nd = 4 \n"
7450//         );
7451//         assert_eq!(editor.text(cx), "a = 1\nb = 2\n\nc = 3\nd = 4 \n");
7452//     });
7453
7454//     // Ensure the new suggestion is displayed when the debounce timeout expires.
7455//     executor.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
7456//     editor.update(cx, |editor, cx| {
7457//         assert!(editor.has_active_copilot_suggestion(cx));
7458//         assert_eq!(
7459//             editor.display_text(cx),
7460//             "\n\na = 1\nb = 2\n\n\n\nc = 3\nd = 4 + c\n"
7461//         );
7462//         assert_eq!(editor.text(cx), "a = 1\nb = 2\n\nc = 3\nd = 4 \n");
7463//     });
7464// }
7465
7466#[gpui::test]
7467async fn test_copilot_disabled_globs(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
7468    init_test(cx, |settings| {
7469        settings
7470            .copilot
7471            .get_or_insert(Default::default())
7472            .disabled_globs = Some(vec![".env*".to_string()]);
7473    });
7474
7475    let (copilot, copilot_lsp) = Copilot::fake(cx);
7476    cx.update(|cx| cx.set_global(copilot));
7477
7478    let fs = FakeFs::new(cx.executor());
7479    fs.insert_tree(
7480        "/test",
7481        json!({
7482            ".env": "SECRET=something\n",
7483            "README.md": "hello\n"
7484        }),
7485    )
7486    .await;
7487    let project = Project::test(fs, ["/test".as_ref()], cx).await;
7488
7489    let private_buffer = project
7490        .update(cx, |project, cx| {
7491            project.open_local_buffer("/test/.env", cx)
7492        })
7493        .await
7494        .unwrap();
7495    let public_buffer = project
7496        .update(cx, |project, cx| {
7497            project.open_local_buffer("/test/README.md", cx)
7498        })
7499        .await
7500        .unwrap();
7501
7502    let multibuffer = cx.build_model(|cx| {
7503        let mut multibuffer = MultiBuffer::new(0);
7504        multibuffer.push_excerpts(
7505            private_buffer.clone(),
7506            [ExcerptRange {
7507                context: Point::new(0, 0)..Point::new(1, 0),
7508                primary: None,
7509            }],
7510            cx,
7511        );
7512        multibuffer.push_excerpts(
7513            public_buffer.clone(),
7514            [ExcerptRange {
7515                context: Point::new(0, 0)..Point::new(1, 0),
7516                primary: None,
7517            }],
7518            cx,
7519        );
7520        multibuffer
7521    });
7522    let editor = cx.add_window(|cx| build_editor(multibuffer, cx));
7523
7524    let mut copilot_requests = copilot_lsp
7525        .handle_request::<copilot::request::GetCompletions, _, _>(move |_params, _cx| async move {
7526            Ok(copilot::request::GetCompletionsResult {
7527                completions: vec![copilot::request::Completion {
7528                    text: "next line".into(),
7529                    range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 0)),
7530                    ..Default::default()
7531                }],
7532            })
7533        });
7534
7535    editor.update(cx, |editor, cx| {
7536        editor.change_selections(None, cx, |selections| {
7537            selections.select_ranges([Point::new(0, 0)..Point::new(0, 0)])
7538        });
7539        editor.next_copilot_suggestion(&Default::default(), cx);
7540    });
7541
7542    executor.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
7543    assert!(copilot_requests.try_next().is_err());
7544
7545    editor.update(cx, |editor, cx| {
7546        editor.change_selections(None, cx, |s| {
7547            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
7548        });
7549        editor.next_copilot_suggestion(&Default::default(), cx);
7550    });
7551
7552    executor.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
7553    assert!(copilot_requests.try_next().is_ok());
7554}
7555
7556#[gpui::test]
7557async fn test_on_type_formatting_not_triggered(cx: &mut gpui::TestAppContext) {
7558    init_test(cx, |_| {});
7559
7560    let mut language = Language::new(
7561        LanguageConfig {
7562            name: "Rust".into(),
7563            path_suffixes: vec!["rs".to_string()],
7564            brackets: BracketPairConfig {
7565                pairs: vec![BracketPair {
7566                    start: "{".to_string(),
7567                    end: "}".to_string(),
7568                    close: true,
7569                    newline: true,
7570                }],
7571                disabled_scopes_by_bracket_ix: Vec::new(),
7572            },
7573            ..Default::default()
7574        },
7575        Some(tree_sitter_rust::language()),
7576    );
7577    let mut fake_servers = language
7578        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
7579            capabilities: lsp::ServerCapabilities {
7580                document_on_type_formatting_provider: Some(lsp::DocumentOnTypeFormattingOptions {
7581                    first_trigger_character: "{".to_string(),
7582                    more_trigger_character: None,
7583                }),
7584                ..Default::default()
7585            },
7586            ..Default::default()
7587        }))
7588        .await;
7589
7590    let fs = FakeFs::new(cx.executor());
7591    fs.insert_tree(
7592        "/a",
7593        json!({
7594            "main.rs": "fn main() { let a = 5; }",
7595            "other.rs": "// Test file",
7596        }),
7597    )
7598    .await;
7599    let project = Project::test(fs, ["/a".as_ref()], cx).await;
7600    project.update(cx, |project, _| project.languages().add(Arc::new(language)));
7601    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
7602
7603    let cx = &mut VisualTestContext::from_window(*workspace, cx);
7604
7605    let worktree_id = workspace
7606        .update(cx, |workspace, cx| {
7607            workspace.project().update(cx, |project, cx| {
7608                project.worktrees().next().unwrap().read(cx).id()
7609            })
7610        })
7611        .unwrap();
7612
7613    let buffer = project
7614        .update(cx, |project, cx| {
7615            project.open_local_buffer("/a/main.rs", cx)
7616        })
7617        .await
7618        .unwrap();
7619    cx.executor().run_until_parked();
7620    cx.executor().start_waiting();
7621    let fake_server = fake_servers.next().await.unwrap();
7622    let editor_handle = workspace
7623        .update(cx, |workspace, cx| {
7624            workspace.open_path((worktree_id, "main.rs"), None, true, cx)
7625        })
7626        .unwrap()
7627        .await
7628        .unwrap()
7629        .downcast::<Editor>()
7630        .unwrap();
7631
7632    fake_server.handle_request::<lsp::request::OnTypeFormatting, _, _>(|params, _| async move {
7633        assert_eq!(
7634            params.text_document_position.text_document.uri,
7635            lsp::Url::from_file_path("/a/main.rs").unwrap(),
7636        );
7637        assert_eq!(
7638            params.text_document_position.position,
7639            lsp::Position::new(0, 21),
7640        );
7641
7642        Ok(Some(vec![lsp::TextEdit {
7643            new_text: "]".to_string(),
7644            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
7645        }]))
7646    });
7647
7648    editor_handle.update(cx, |editor, cx| {
7649        editor.focus(cx);
7650        editor.change_selections(None, cx, |s| {
7651            s.select_ranges([Point::new(0, 21)..Point::new(0, 20)])
7652        });
7653        editor.handle_input("{", cx);
7654    });
7655
7656    cx.executor().run_until_parked();
7657
7658    buffer.update(cx, |buffer, _| {
7659        assert_eq!(
7660            buffer.text(),
7661            "fn main() { let a = {5}; }",
7662            "No extra braces from on type formatting should appear in the buffer"
7663        )
7664    });
7665}
7666
7667#[gpui::test]
7668async fn test_language_server_restart_due_to_settings_change(cx: &mut gpui::TestAppContext) {
7669    init_test(cx, |_| {});
7670
7671    let language_name: Arc<str> = "Rust".into();
7672    let mut language = Language::new(
7673        LanguageConfig {
7674            name: Arc::clone(&language_name),
7675            path_suffixes: vec!["rs".to_string()],
7676            ..Default::default()
7677        },
7678        Some(tree_sitter_rust::language()),
7679    );
7680
7681    let server_restarts = Arc::new(AtomicUsize::new(0));
7682    let closure_restarts = Arc::clone(&server_restarts);
7683    let language_server_name = "test language server";
7684    let mut fake_servers = language
7685        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
7686            name: language_server_name,
7687            initialization_options: Some(json!({
7688                "testOptionValue": true
7689            })),
7690            initializer: Some(Box::new(move |fake_server| {
7691                let task_restarts = Arc::clone(&closure_restarts);
7692                fake_server.handle_request::<lsp::request::Shutdown, _, _>(move |_, _| {
7693                    task_restarts.fetch_add(1, atomic::Ordering::Release);
7694                    futures::future::ready(Ok(()))
7695                });
7696            })),
7697            ..Default::default()
7698        }))
7699        .await;
7700
7701    let fs = FakeFs::new(cx.executor());
7702    fs.insert_tree(
7703        "/a",
7704        json!({
7705            "main.rs": "fn main() { let a = 5; }",
7706            "other.rs": "// Test file",
7707        }),
7708    )
7709    .await;
7710    let project = Project::test(fs, ["/a".as_ref()], cx).await;
7711    project.update(cx, |project, _| project.languages().add(Arc::new(language)));
7712    let _window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
7713    let _buffer = project
7714        .update(cx, |project, cx| {
7715            project.open_local_buffer("/a/main.rs", cx)
7716        })
7717        .await
7718        .unwrap();
7719    let _fake_server = fake_servers.next().await.unwrap();
7720    update_test_language_settings(cx, |language_settings| {
7721        language_settings.languages.insert(
7722            Arc::clone(&language_name),
7723            LanguageSettingsContent {
7724                tab_size: NonZeroU32::new(8),
7725                ..Default::default()
7726            },
7727        );
7728    });
7729    cx.executor().run_until_parked();
7730    assert_eq!(
7731        server_restarts.load(atomic::Ordering::Acquire),
7732        0,
7733        "Should not restart LSP server on an unrelated change"
7734    );
7735
7736    update_test_project_settings(cx, |project_settings| {
7737        project_settings.lsp.insert(
7738            "Some other server name".into(),
7739            LspSettings {
7740                initialization_options: Some(json!({
7741                    "some other init value": false
7742                })),
7743            },
7744        );
7745    });
7746    cx.executor().run_until_parked();
7747    assert_eq!(
7748        server_restarts.load(atomic::Ordering::Acquire),
7749        0,
7750        "Should not restart LSP server on an unrelated LSP settings change"
7751    );
7752
7753    update_test_project_settings(cx, |project_settings| {
7754        project_settings.lsp.insert(
7755            language_server_name.into(),
7756            LspSettings {
7757                initialization_options: Some(json!({
7758                    "anotherInitValue": false
7759                })),
7760            },
7761        );
7762    });
7763    cx.executor().run_until_parked();
7764    assert_eq!(
7765        server_restarts.load(atomic::Ordering::Acquire),
7766        1,
7767        "Should restart LSP server on a related LSP settings change"
7768    );
7769
7770    update_test_project_settings(cx, |project_settings| {
7771        project_settings.lsp.insert(
7772            language_server_name.into(),
7773            LspSettings {
7774                initialization_options: Some(json!({
7775                    "anotherInitValue": false
7776                })),
7777            },
7778        );
7779    });
7780    cx.executor().run_until_parked();
7781    assert_eq!(
7782        server_restarts.load(atomic::Ordering::Acquire),
7783        1,
7784        "Should not restart LSP server on a related LSP settings change that is the same"
7785    );
7786
7787    update_test_project_settings(cx, |project_settings| {
7788        project_settings.lsp.insert(
7789            language_server_name.into(),
7790            LspSettings {
7791                initialization_options: None,
7792            },
7793        );
7794    });
7795    cx.executor().run_until_parked();
7796    assert_eq!(
7797        server_restarts.load(atomic::Ordering::Acquire),
7798        2,
7799        "Should restart LSP server on another related LSP settings change"
7800    );
7801}
7802
7803#[gpui::test]
7804async fn test_completions_with_additional_edits(cx: &mut gpui::TestAppContext) {
7805    init_test(cx, |_| {});
7806
7807    let mut cx = EditorLspTestContext::new_rust(
7808        lsp::ServerCapabilities {
7809            completion_provider: Some(lsp::CompletionOptions {
7810                trigger_characters: Some(vec![".".to_string()]),
7811                resolve_provider: Some(true),
7812                ..Default::default()
7813            }),
7814            ..Default::default()
7815        },
7816        cx,
7817    )
7818    .await;
7819
7820    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
7821    cx.simulate_keystroke(".");
7822    let completion_item = lsp::CompletionItem {
7823        label: "some".into(),
7824        kind: Some(lsp::CompletionItemKind::SNIPPET),
7825        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
7826        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
7827            kind: lsp::MarkupKind::Markdown,
7828            value: "```rust\nSome(2)\n```".to_string(),
7829        })),
7830        deprecated: Some(false),
7831        sort_text: Some("fffffff2".to_string()),
7832        filter_text: Some("some".to_string()),
7833        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
7834        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
7835            range: lsp::Range {
7836                start: lsp::Position {
7837                    line: 0,
7838                    character: 22,
7839                },
7840                end: lsp::Position {
7841                    line: 0,
7842                    character: 22,
7843                },
7844            },
7845            new_text: "Some(2)".to_string(),
7846        })),
7847        additional_text_edits: Some(vec![lsp::TextEdit {
7848            range: lsp::Range {
7849                start: lsp::Position {
7850                    line: 0,
7851                    character: 20,
7852                },
7853                end: lsp::Position {
7854                    line: 0,
7855                    character: 22,
7856                },
7857            },
7858            new_text: "".to_string(),
7859        }]),
7860        ..Default::default()
7861    };
7862
7863    let closure_completion_item = completion_item.clone();
7864    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
7865        let task_completion_item = closure_completion_item.clone();
7866        async move {
7867            Ok(Some(lsp::CompletionResponse::Array(vec![
7868                task_completion_item,
7869            ])))
7870        }
7871    });
7872
7873    request.next().await;
7874
7875    cx.condition(|editor, _| editor.context_menu_visible())
7876        .await;
7877    let apply_additional_edits = cx.update_editor(|editor, cx| {
7878        editor
7879            .confirm_completion(&ConfirmCompletion::default(), cx)
7880            .unwrap()
7881    });
7882    cx.assert_editor_state(indoc! {"fn main() { let a = 2.Some(2)ˇ; }"});
7883
7884    cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
7885        let task_completion_item = completion_item.clone();
7886        async move { Ok(task_completion_item) }
7887    })
7888    .next()
7889    .await
7890    .unwrap();
7891    apply_additional_edits.await.unwrap();
7892    cx.assert_editor_state(indoc! {"fn main() { let a = Some(2)ˇ; }"});
7893}
7894
7895#[gpui::test]
7896async fn test_completions_in_languages_with_extra_word_characters(cx: &mut gpui::TestAppContext) {
7897    init_test(cx, |_| {});
7898
7899    let mut cx = EditorLspTestContext::new(
7900        Language::new(
7901            LanguageConfig {
7902                path_suffixes: vec!["jsx".into()],
7903                overrides: [(
7904                    "element".into(),
7905                    LanguageConfigOverride {
7906                        word_characters: Override::Set(['-'].into_iter().collect()),
7907                        ..Default::default()
7908                    },
7909                )]
7910                .into_iter()
7911                .collect(),
7912                ..Default::default()
7913            },
7914            Some(tree_sitter_typescript::language_tsx()),
7915        )
7916        .with_override_query("(jsx_self_closing_element) @element")
7917        .unwrap(),
7918        lsp::ServerCapabilities {
7919            completion_provider: Some(lsp::CompletionOptions {
7920                trigger_characters: Some(vec![":".to_string()]),
7921                ..Default::default()
7922            }),
7923            ..Default::default()
7924        },
7925        cx,
7926    )
7927    .await;
7928
7929    cx.lsp
7930        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
7931            Ok(Some(lsp::CompletionResponse::Array(vec![
7932                lsp::CompletionItem {
7933                    label: "bg-blue".into(),
7934                    ..Default::default()
7935                },
7936                lsp::CompletionItem {
7937                    label: "bg-red".into(),
7938                    ..Default::default()
7939                },
7940                lsp::CompletionItem {
7941                    label: "bg-yellow".into(),
7942                    ..Default::default()
7943                },
7944            ])))
7945        });
7946
7947    cx.set_state(r#"<p class="bgˇ" />"#);
7948
7949    // Trigger completion when typing a dash, because the dash is an extra
7950    // word character in the 'element' scope, which contains the cursor.
7951    cx.simulate_keystroke("-");
7952    cx.executor().run_until_parked();
7953    cx.update_editor(|editor, _| {
7954        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
7955            assert_eq!(
7956                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
7957                &["bg-red", "bg-blue", "bg-yellow"]
7958            );
7959        } else {
7960            panic!("expected completion menu to be open");
7961        }
7962    });
7963
7964    cx.simulate_keystroke("l");
7965    cx.executor().run_until_parked();
7966    cx.update_editor(|editor, _| {
7967        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
7968            assert_eq!(
7969                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
7970                &["bg-blue", "bg-yellow"]
7971            );
7972        } else {
7973            panic!("expected completion menu to be open");
7974        }
7975    });
7976
7977    // When filtering completions, consider the character after the '-' to
7978    // be the start of a subword.
7979    cx.set_state(r#"<p class="yelˇ" />"#);
7980    cx.simulate_keystroke("l");
7981    cx.executor().run_until_parked();
7982    cx.update_editor(|editor, _| {
7983        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
7984            assert_eq!(
7985                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
7986                &["bg-yellow"]
7987            );
7988        } else {
7989            panic!("expected completion menu to be open");
7990        }
7991    });
7992}
7993
7994#[gpui::test]
7995async fn test_document_format_with_prettier(cx: &mut gpui::TestAppContext) {
7996    init_test(cx, |settings| {
7997        settings.defaults.formatter = Some(language_settings::Formatter::Prettier)
7998    });
7999
8000    let mut language = Language::new(
8001        LanguageConfig {
8002            name: "Rust".into(),
8003            path_suffixes: vec!["rs".to_string()],
8004            prettier_parser_name: Some("test_parser".to_string()),
8005            ..Default::default()
8006        },
8007        Some(tree_sitter_rust::language()),
8008    );
8009
8010    let test_plugin = "test_plugin";
8011    let _ = language
8012        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
8013            prettier_plugins: vec![test_plugin],
8014            ..Default::default()
8015        }))
8016        .await;
8017
8018    let fs = FakeFs::new(cx.executor());
8019    fs.insert_file("/file.rs", Default::default()).await;
8020
8021    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
8022    let prettier_format_suffix = project::TEST_PRETTIER_FORMAT_SUFFIX;
8023    project.update(cx, |project, _| {
8024        project.languages().add(Arc::new(language));
8025    });
8026    let buffer = project
8027        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
8028        .await
8029        .unwrap();
8030
8031    let buffer_text = "one\ntwo\nthree\n";
8032    let buffer = cx.build_model(|cx| MultiBuffer::singleton(buffer, cx));
8033    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
8034    editor.update(cx, |editor, cx| editor.set_text(buffer_text, cx));
8035
8036    editor
8037        .update(cx, |editor, cx| {
8038            editor.perform_format(project.clone(), FormatTrigger::Manual, cx)
8039        })
8040        .unwrap()
8041        .await;
8042    assert_eq!(
8043        editor.update(cx, |editor, cx| editor.text(cx)),
8044        buffer_text.to_string() + prettier_format_suffix,
8045        "Test prettier formatting was not applied to the original buffer text",
8046    );
8047
8048    update_test_language_settings(cx, |settings| {
8049        settings.defaults.formatter = Some(language_settings::Formatter::Auto)
8050    });
8051    let format = editor.update(cx, |editor, cx| {
8052        editor.perform_format(project.clone(), FormatTrigger::Manual, cx)
8053    });
8054    format.await.unwrap();
8055    assert_eq!(
8056        editor.update(cx, |editor, cx| editor.text(cx)),
8057        buffer_text.to_string() + prettier_format_suffix + "\n" + prettier_format_suffix,
8058        "Autoformatting (via test prettier) was not applied to the original buffer text",
8059    );
8060}
8061
8062fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
8063    let point = DisplayPoint::new(row as u32, column as u32);
8064    point..point
8065}
8066
8067fn assert_selection_ranges(marked_text: &str, view: &mut Editor, cx: &mut ViewContext<Editor>) {
8068    let (text, ranges) = marked_text_ranges(marked_text, true);
8069    assert_eq!(view.text(cx), text);
8070    assert_eq!(
8071        view.selections.ranges(cx),
8072        ranges,
8073        "Assert selections are {}",
8074        marked_text
8075    );
8076}
8077
8078/// Handle completion request passing a marked string specifying where the completion
8079/// should be triggered from using '|' character, what range should be replaced, and what completions
8080/// should be returned using '<' and '>' to delimit the range
8081pub fn handle_completion_request<'a>(
8082    cx: &mut EditorLspTestContext<'a>,
8083    marked_string: &str,
8084    completions: Vec<&'static str>,
8085) -> impl Future<Output = ()> {
8086    let complete_from_marker: TextRangeMarker = '|'.into();
8087    let replace_range_marker: TextRangeMarker = ('<', '>').into();
8088    let (_, mut marked_ranges) = marked_text_ranges_by(
8089        marked_string,
8090        vec![complete_from_marker.clone(), replace_range_marker.clone()],
8091    );
8092
8093    let complete_from_position =
8094        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
8095    let replace_range =
8096        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
8097
8098    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |url, params, _| {
8099        let completions = completions.clone();
8100        async move {
8101            assert_eq!(params.text_document_position.text_document.uri, url.clone());
8102            assert_eq!(
8103                params.text_document_position.position,
8104                complete_from_position
8105            );
8106            Ok(Some(lsp::CompletionResponse::Array(
8107                completions
8108                    .iter()
8109                    .map(|completion_text| lsp::CompletionItem {
8110                        label: completion_text.to_string(),
8111                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
8112                            range: replace_range,
8113                            new_text: completion_text.to_string(),
8114                        })),
8115                        ..Default::default()
8116                    })
8117                    .collect(),
8118            )))
8119        }
8120    });
8121
8122    async move {
8123        request.next().await;
8124    }
8125}
8126
8127fn handle_resolve_completion_request<'a>(
8128    cx: &mut EditorLspTestContext<'a>,
8129    edits: Option<Vec<(&'static str, &'static str)>>,
8130) -> impl Future<Output = ()> {
8131    let edits = edits.map(|edits| {
8132        edits
8133            .iter()
8134            .map(|(marked_string, new_text)| {
8135                let (_, marked_ranges) = marked_text_ranges(marked_string, false);
8136                let replace_range = cx.to_lsp_range(marked_ranges[0].clone());
8137                lsp::TextEdit::new(replace_range, new_text.to_string())
8138            })
8139            .collect::<Vec<_>>()
8140    });
8141
8142    let mut request =
8143        cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
8144            let edits = edits.clone();
8145            async move {
8146                Ok(lsp::CompletionItem {
8147                    additional_text_edits: edits,
8148                    ..Default::default()
8149                })
8150            }
8151        });
8152
8153    async move {
8154        request.next().await;
8155    }
8156}
8157
8158fn handle_copilot_completion_request(
8159    lsp: &lsp::FakeLanguageServer,
8160    completions: Vec<copilot::request::Completion>,
8161    completions_cycling: Vec<copilot::request::Completion>,
8162) {
8163    lsp.handle_request::<copilot::request::GetCompletions, _, _>(move |_params, _cx| {
8164        let completions = completions.clone();
8165        async move {
8166            Ok(copilot::request::GetCompletionsResult {
8167                completions: completions.clone(),
8168            })
8169        }
8170    });
8171    lsp.handle_request::<copilot::request::GetCompletionsCycling, _, _>(move |_params, _cx| {
8172        let completions_cycling = completions_cycling.clone();
8173        async move {
8174            Ok(copilot::request::GetCompletionsResult {
8175                completions: completions_cycling.clone(),
8176            })
8177        }
8178    });
8179}
8180
8181pub(crate) fn update_test_language_settings(
8182    cx: &mut TestAppContext,
8183    f: impl Fn(&mut AllLanguageSettingsContent),
8184) {
8185    cx.update(|cx| {
8186        cx.update_global(|store: &mut SettingsStore, cx| {
8187            store.update_user_settings::<AllLanguageSettings>(cx, f);
8188        });
8189    });
8190}
8191
8192pub(crate) fn update_test_project_settings(
8193    cx: &mut TestAppContext,
8194    f: impl Fn(&mut ProjectSettings),
8195) {
8196    cx.update(|cx| {
8197        cx.update_global(|store: &mut SettingsStore, cx| {
8198            store.update_user_settings::<ProjectSettings>(cx, f);
8199        });
8200    });
8201}
8202
8203pub(crate) fn init_test(cx: &mut TestAppContext, f: fn(&mut AllLanguageSettingsContent)) {
8204    cx.update(|cx| {
8205        let store = SettingsStore::test(cx);
8206        cx.set_global(store);
8207        theme::init(theme::LoadThemes::JustBase, cx);
8208        client::init_settings(cx);
8209        language::init(cx);
8210        Project::init_settings(cx);
8211        workspace::init_settings(cx);
8212        crate::init(cx);
8213    });
8214
8215    update_test_language_settings(cx, f);
8216}