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