editor_tests.rs

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