editor_tests.rs

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