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