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