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