console.rs

   1#![expect(clippy::result_large_err)]
   2use crate::{
   3    tests::{active_debug_session_panel, start_debug_session},
   4    *,
   5};
   6use dap::requests::StackTrace;
   7use editor::{DisplayPoint, display_map::DisplayRow};
   8use gpui::{BackgroundExecutor, TestAppContext, VisualTestContext};
   9use project::{FakeFs, Project};
  10use serde_json::json;
  11use tests::{init_test, init_test_workspace};
  12use util::path;
  13
  14#[gpui::test]
  15async fn test_handle_output_event(executor: BackgroundExecutor, cx: &mut TestAppContext) {
  16    init_test(cx);
  17
  18    let fs = FakeFs::new(executor.clone());
  19
  20    fs.insert_tree(
  21        path!("/project"),
  22        json!({
  23            "main.rs": "First line\nSecond line\nThird line\nFourth line",
  24        }),
  25    )
  26    .await;
  27
  28    let project = Project::test(fs, [path!("/project").as_ref()], cx).await;
  29    let workspace = init_test_workspace(&project, cx).await;
  30    let cx = &mut VisualTestContext::from_window(*workspace, cx);
  31    workspace
  32        .update(cx, |workspace, window, cx| {
  33            workspace.focus_panel::<DebugPanel>(window, cx);
  34        })
  35        .unwrap();
  36
  37    let session = start_debug_session(&workspace, cx, |_| {}).unwrap();
  38    let client = session.read_with(cx, |session, _| session.adapter_client().unwrap());
  39
  40    client.on_request::<StackTrace, _>(move |_, _| {
  41        Ok(dap::StackTraceResponse {
  42            stack_frames: Vec::default(),
  43            total_frames: None,
  44        })
  45    });
  46
  47    client
  48        .fake_event(dap::messages::Events::Output(dap::OutputEvent {
  49            category: None,
  50            output: "First console output line before thread stopped!".to_string(),
  51            data: None,
  52            variables_reference: None,
  53            source: None,
  54            line: None,
  55            column: None,
  56            group: None,
  57            location_reference: None,
  58        }))
  59        .await;
  60
  61    client
  62        .fake_event(dap::messages::Events::Output(dap::OutputEvent {
  63            category: Some(dap::OutputEventCategory::Stdout),
  64            output: "First output line before thread stopped!".to_string(),
  65            data: None,
  66            variables_reference: None,
  67            source: None,
  68            line: None,
  69            column: None,
  70            group: None,
  71            location_reference: None,
  72        }))
  73        .await;
  74
  75    client
  76        .fake_event(dap::messages::Events::Stopped(dap::StoppedEvent {
  77            reason: dap::StoppedEventReason::Pause,
  78            description: None,
  79            thread_id: Some(1),
  80            preserve_focus_hint: None,
  81            text: None,
  82            all_threads_stopped: None,
  83            hit_breakpoint_ids: None,
  84        }))
  85        .await;
  86
  87    cx.run_until_parked();
  88
  89    let running_state =
  90        active_debug_session_panel(workspace, cx).update_in(cx, |item, window, cx| {
  91            cx.focus_self(window);
  92            item.running_state().clone()
  93        });
  94
  95    cx.run_until_parked();
  96
  97    // assert we have output from before the thread stopped
  98    workspace
  99        .update(cx, |workspace, _window, cx| {
 100            let debug_panel = workspace.panel::<DebugPanel>(cx).unwrap();
 101            let active_debug_session_panel = debug_panel
 102                .update(cx, |this, _| this.active_session())
 103                .unwrap();
 104
 105            assert_eq!(
 106                "First console output line before thread stopped!\nFirst output line before thread stopped!\n",
 107                active_debug_session_panel.read(cx).running_state().read(cx).console().read(cx).editor().read(cx).text(cx).as_str()
 108            );
 109        })
 110        .unwrap();
 111
 112    client
 113        .fake_event(dap::messages::Events::Output(dap::OutputEvent {
 114            category: Some(dap::OutputEventCategory::Stdout),
 115            output: "\tSecond output line after thread stopped!".to_string(),
 116            data: None,
 117            variables_reference: None,
 118            source: None,
 119            line: None,
 120            column: None,
 121            group: None,
 122            location_reference: None,
 123        }))
 124        .await;
 125
 126    client
 127        .fake_event(dap::messages::Events::Output(dap::OutputEvent {
 128            category: Some(dap::OutputEventCategory::Console),
 129            output: "\tSecond console output line after thread stopped!".to_string(),
 130            data: None,
 131            variables_reference: None,
 132            source: None,
 133            line: None,
 134            column: None,
 135            group: None,
 136            location_reference: None,
 137        }))
 138        .await;
 139
 140    cx.run_until_parked();
 141    running_state.update(cx, |_, cx| {
 142        cx.refresh_windows();
 143    });
 144    cx.run_until_parked();
 145
 146    // assert we have output from before and after the thread stopped
 147    workspace
 148        .update(cx, |workspace, _window, cx| {
 149            let debug_panel = workspace.panel::<DebugPanel>(cx).unwrap();
 150            let active_session_panel = debug_panel
 151                .update(cx, |this, _| this.active_session())
 152                .unwrap();
 153
 154            assert_eq!(
 155                "First console output line before thread stopped!\nFirst output line before thread stopped!\n\tSecond output line after thread stopped!\n\tSecond console output line after thread stopped!\n",
 156                active_session_panel.read(cx).running_state().read(cx).console().read(cx).editor().read(cx).text(cx).as_str()
 157            );
 158        })
 159        .unwrap();
 160}
 161
 162#[gpui::test]
 163async fn test_escape_code_processing(executor: BackgroundExecutor, cx: &mut TestAppContext) {
 164    init_test(cx);
 165
 166    let fs = FakeFs::new(executor.clone());
 167
 168    fs.insert_tree(
 169        path!("/project"),
 170        json!({
 171            "main.rs": "First line\nSecond line\nThird line\nFourth line",
 172        }),
 173    )
 174    .await;
 175
 176    let project = Project::test(fs, [path!("/project").as_ref()], cx).await;
 177    let workspace = init_test_workspace(&project, cx).await;
 178    let cx = &mut VisualTestContext::from_window(*workspace, cx);
 179    workspace
 180        .update(cx, |workspace, window, cx| {
 181            workspace.focus_panel::<DebugPanel>(window, cx);
 182        })
 183        .unwrap();
 184
 185    let session = start_debug_session(&workspace, cx, |_| {}).unwrap();
 186    let client = session.read_with(cx, |session, _| session.adapter_client().unwrap());
 187
 188    client.on_request::<StackTrace, _>(move |_, _| {
 189        Ok(dap::StackTraceResponse {
 190            stack_frames: Vec::default(),
 191            total_frames: None,
 192        })
 193    });
 194
 195    client
 196        .fake_event(dap::messages::Events::Output(dap::OutputEvent {
 197            category: None,
 198            output: "Checking latest version of JavaScript...".to_string(),
 199            data: None,
 200            variables_reference: None,
 201            source: None,
 202            line: None,
 203            column: None,
 204            group: None,
 205            location_reference: None,
 206        }))
 207        .await;
 208    client
 209        .fake_event(dap::messages::Events::Output(dap::OutputEvent {
 210            category: None,
 211            output: "   \u{1b}[1m\u{1b}[38;2;173;127;168m▲ Next.js 15.1.5\u{1b}[39m\u{1b}[22m"
 212                .to_string(),
 213            data: None,
 214            variables_reference: None,
 215            source: None,
 216            line: None,
 217            column: None,
 218            group: None,
 219            location_reference: None,
 220        }))
 221        .await;
 222    client
 223        .fake_event(dap::messages::Events::Output(dap::OutputEvent {
 224            category: None,
 225            output: "   - Local:        http://localhost:3000\n   - Network:      http://192.168.1.144:3000\n\n \u{1b}[32m\u{1b}[1m✓\u{1b}[22m\u{1b}[39m Starting..."
 226                .to_string(),
 227            data: None,
 228            variables_reference: None,
 229            source: None,
 230            line: None,
 231            column: None,
 232            group: None,
 233            location_reference: None,
 234        }))
 235        .await;
 236    client
 237        .fake_event(dap::messages::Events::Output(dap::OutputEvent {
 238            category: None,
 239            output: "Something else...".to_string(),
 240            data: None,
 241            variables_reference: None,
 242            source: None,
 243            line: None,
 244            column: None,
 245            group: None,
 246            location_reference: None,
 247        }))
 248        .await;
 249    client
 250        .fake_event(dap::messages::Events::Output(dap::OutputEvent {
 251            category: None,
 252            output: " \u{1b}[32m\u{1b}[1m✓\u{1b}[22m\u{1b}[39m Ready in 1009ms\n".to_string(),
 253            data: None,
 254            variables_reference: None,
 255            source: None,
 256            line: None,
 257            column: None,
 258            group: None,
 259            location_reference: None,
 260        }))
 261        .await;
 262
 263    client
 264        .fake_event(dap::messages::Events::Output(dap::OutputEvent {
 265            category: None,
 266            output: "\u{1b}[41m\u{1b}[37mBoth background and foreground!".to_string(),
 267            data: None,
 268            variables_reference: None,
 269            source: None,
 270            line: None,
 271            column: None,
 272            group: None,
 273            location_reference: None,
 274        }))
 275        .await;
 276    client
 277        .fake_event(dap::messages::Events::Output(dap::OutputEvent {
 278            category: None,
 279            output: "Even more...".to_string(),
 280            data: None,
 281            variables_reference: None,
 282            source: None,
 283            line: None,
 284            column: None,
 285            group: None,
 286            location_reference: None,
 287        }))
 288        .await;
 289
 290    cx.run_until_parked();
 291
 292    let _running_state =
 293        active_debug_session_panel(workspace, cx).update_in(cx, |item, window, cx| {
 294            cx.focus_self(window);
 295            item.running_state().update(cx, |this, cx| {
 296                this.console()
 297                    .update(cx, |this, cx| this.update_output(window, cx));
 298            });
 299
 300            item.running_state().clone()
 301        });
 302
 303    cx.run_until_parked();
 304
 305    workspace
 306        .update(cx, |workspace, window, cx| {
 307            let debug_panel = workspace.panel::<DebugPanel>(cx).unwrap();
 308            let active_debug_session_panel = debug_panel
 309                .update(cx, |this, _| this.active_session())
 310                .unwrap();
 311
 312            let editor =
 313                active_debug_session_panel
 314                    .read(cx)
 315                    .running_state()
 316                    .read(cx)
 317                    .console()
 318                    .read(cx)
 319                    .editor().clone();
 320
 321            assert_eq!(
 322                "Checking latest version of JavaScript...\n   ▲ Next.js 15.1.5\n   - Local:        http://localhost:3000\n   - Network:      http://192.168.1.144:3000\n\n ✓ Starting...\nSomething else...\n ✓ Ready in 1009ms\nBoth background and foreground!\nEven more...\n",
 323                editor
 324                    .read(cx)
 325                    .text(cx)
 326                    .as_str()
 327            );
 328
 329            let text_highlights = editor.update(cx, |editor, cx| {
 330                let mut text_highlights = editor.all_text_highlights(window, cx).into_iter().flat_map(|(_, ranges)| ranges).collect::<Vec<_>>();
 331                text_highlights.sort_by(|a, b| a.start.cmp(&b.start));
 332                text_highlights
 333            });
 334            pretty_assertions::assert_eq!(
 335                text_highlights,
 336                [
 337                    DisplayPoint::new(DisplayRow(1), 3)..DisplayPoint::new(DisplayRow(1), 21),
 338                    DisplayPoint::new(DisplayRow(1), 21)..DisplayPoint::new(DisplayRow(2), 0),
 339                    DisplayPoint::new(DisplayRow(5), 1)..DisplayPoint::new(DisplayRow(5), 4),
 340                    DisplayPoint::new(DisplayRow(5), 4)..DisplayPoint::new(DisplayRow(6), 0),
 341                    DisplayPoint::new(DisplayRow(7), 1)..DisplayPoint::new(DisplayRow(7), 4),
 342                    DisplayPoint::new(DisplayRow(7), 4)..DisplayPoint::new(DisplayRow(8), 0),
 343                    DisplayPoint::new(DisplayRow(8), 0)..DisplayPoint::new(DisplayRow(9), 0),
 344                ]
 345            );
 346
 347            let background_highlights = editor.update(cx, |editor, cx| {
 348                editor.all_text_background_highlights(window, cx).into_iter().map(|(range, _)| range).collect::<Vec<_>>()
 349            });
 350            pretty_assertions::assert_eq!(
 351                background_highlights,
 352                [
 353                    DisplayPoint::new(DisplayRow(8), 0)..DisplayPoint::new(DisplayRow(9), 0),
 354                ]
 355            )
 356        })
 357        .unwrap();
 358}
 359
 360// #[gpui::test]
 361// async fn test_grouped_output(executor: BackgroundExecutor, cx: &mut TestAppContext) {
 362//     init_test(cx);
 363
 364//     let fs = FakeFs::new(executor.clone());
 365
 366//     fs.insert_tree(
 367//         "/project",
 368//         json!({
 369//             "main.rs": "First line\nSecond line\nThird line\nFourth line",
 370//         }),
 371//     )
 372//     .await;
 373
 374//     let project = Project::test(fs, ["/project".as_ref()], cx).await;
 375//     let workspace = init_test_workspace(&project, cx).await;
 376//     let cx = &mut VisualTestContext::from_window(*workspace, cx);
 377
 378//     let task = project.update(cx, |project, cx| {
 379//         project.start_debug_session(
 380//             dap::test_config(dap::DebugRequestType::Launch, None, None),
 381//             cx,
 382//         )
 383//     });
 384
 385//     let session = task.await.unwrap();
 386//     let client = session.update(cx, |session, _| session.adapter_client().unwrap());
 387
 388//     client
 389//         .on_request::<StackTrace, _>(move |_, _| {
 390//             Ok(dap::StackTraceResponse {
 391//                 stack_frames: Vec::default(),
 392//                 total_frames: None,
 393//             })
 394//         })
 395//         .await;
 396
 397//     client
 398//         .fake_event(dap::messages::Events::Stopped(dap::StoppedEvent {
 399//             reason: dap::StoppedEventReason::Pause,
 400//             description: None,
 401//             thread_id: Some(1),
 402//             preserve_focus_hint: None,
 403//             text: None,
 404//             all_threads_stopped: None,
 405//             hit_breakpoint_ids: None,
 406//         }))
 407//         .await;
 408
 409//     client
 410//         .fake_event(dap::messages::Events::Output(dap::OutputEvent {
 411//             category: None,
 412//             output: "First line".to_string(),
 413//             data: None,
 414//             variables_reference: None,
 415//             source: None,
 416//             line: None,
 417//             column: None,
 418//             group: None,
 419//             location_reference: None,
 420//         }))
 421//         .await;
 422
 423//     client
 424//         .fake_event(dap::messages::Events::Output(dap::OutputEvent {
 425//             category: Some(dap::OutputEventCategory::Stdout),
 426//             output: "First group".to_string(),
 427//             data: None,
 428//             variables_reference: None,
 429//             source: None,
 430//             line: None,
 431//             column: None,
 432//             group: Some(dap::OutputEventGroup::Start),
 433//             location_reference: None,
 434//         }))
 435//         .await;
 436
 437//     client
 438//         .fake_event(dap::messages::Events::Output(dap::OutputEvent {
 439//             category: Some(dap::OutputEventCategory::Stdout),
 440//             output: "First item in group 1".to_string(),
 441//             data: None,
 442//             variables_reference: None,
 443//             source: None,
 444//             line: None,
 445//             column: None,
 446//             group: None,
 447//             location_reference: None,
 448//         }))
 449//         .await;
 450
 451//     client
 452//         .fake_event(dap::messages::Events::Output(dap::OutputEvent {
 453//             category: Some(dap::OutputEventCategory::Stdout),
 454//             output: "Second item in group 1".to_string(),
 455//             data: None,
 456//             variables_reference: None,
 457//             source: None,
 458//             line: None,
 459//             column: None,
 460//             group: None,
 461//             location_reference: None,
 462//         }))
 463//         .await;
 464
 465//     client
 466//         .fake_event(dap::messages::Events::Output(dap::OutputEvent {
 467//             category: Some(dap::OutputEventCategory::Stdout),
 468//             output: "Second group".to_string(),
 469//             data: None,
 470//             variables_reference: None,
 471//             source: None,
 472//             line: None,
 473//             column: None,
 474//             group: Some(dap::OutputEventGroup::Start),
 475//             location_reference: None,
 476//         }))
 477//         .await;
 478
 479//     client
 480//         .fake_event(dap::messages::Events::Output(dap::OutputEvent {
 481//             category: Some(dap::OutputEventCategory::Stdout),
 482//             output: "First item in group 2".to_string(),
 483//             data: None,
 484//             variables_reference: None,
 485//             source: None,
 486//             line: None,
 487//             column: None,
 488//             group: None,
 489//             location_reference: None,
 490//         }))
 491//         .await;
 492
 493//     client
 494//         .fake_event(dap::messages::Events::Output(dap::OutputEvent {
 495//             category: Some(dap::OutputEventCategory::Stdout),
 496//             output: "Second item in group 2".to_string(),
 497//             data: None,
 498//             variables_reference: None,
 499//             source: None,
 500//             line: None,
 501//             column: None,
 502//             group: None,
 503//             location_reference: None,
 504//         }))
 505//         .await;
 506
 507//     client
 508//         .fake_event(dap::messages::Events::Output(dap::OutputEvent {
 509//             category: Some(dap::OutputEventCategory::Stdout),
 510//             output: "End group 2".to_string(),
 511//             data: None,
 512//             variables_reference: None,
 513//             source: None,
 514//             line: None,
 515//             column: None,
 516//             group: Some(dap::OutputEventGroup::End),
 517//             location_reference: None,
 518//         }))
 519//         .await;
 520
 521//     client
 522//         .fake_event(dap::messages::Events::Output(dap::OutputEvent {
 523//             category: Some(dap::OutputEventCategory::Stdout),
 524//             output: "Third group".to_string(),
 525//             data: None,
 526//             variables_reference: None,
 527//             source: None,
 528//             line: None,
 529//             column: None,
 530//             group: Some(dap::OutputEventGroup::StartCollapsed),
 531//             location_reference: None,
 532//         }))
 533//         .await;
 534
 535//     client
 536//         .fake_event(dap::messages::Events::Output(dap::OutputEvent {
 537//             category: Some(dap::OutputEventCategory::Stdout),
 538//             output: "First item in group 3".to_string(),
 539//             data: None,
 540//             variables_reference: None,
 541//             source: None,
 542//             line: None,
 543//             column: None,
 544//             group: None,
 545//             location_reference: None,
 546//         }))
 547//         .await;
 548
 549//     client
 550//         .fake_event(dap::messages::Events::Output(dap::OutputEvent {
 551//             category: Some(dap::OutputEventCategory::Stdout),
 552//             output: "Second item in group 3".to_string(),
 553//             data: None,
 554//             variables_reference: None,
 555//             source: None,
 556//             line: None,
 557//             column: None,
 558//             group: None,
 559//             location_reference: None,
 560//         }))
 561//         .await;
 562
 563//     client
 564//         .fake_event(dap::messages::Events::Output(dap::OutputEvent {
 565//             category: Some(dap::OutputEventCategory::Stdout),
 566//             output: "End group 3".to_string(),
 567//             data: None,
 568//             variables_reference: None,
 569//             source: None,
 570//             line: None,
 571//             column: None,
 572//             group: Some(dap::OutputEventGroup::End),
 573//             location_reference: None,
 574//         }))
 575//         .await;
 576
 577//     client
 578//         .fake_event(dap::messages::Events::Output(dap::OutputEvent {
 579//             category: Some(dap::OutputEventCategory::Stdout),
 580//             output: "Third item in group 1".to_string(),
 581//             data: None,
 582//             variables_reference: None,
 583//             source: None,
 584//             line: None,
 585//             column: None,
 586//             group: None,
 587//             location_reference: None,
 588//         }))
 589//         .await;
 590
 591//     client
 592//         .fake_event(dap::messages::Events::Output(dap::OutputEvent {
 593//             category: Some(dap::OutputEventCategory::Stdout),
 594//             output: "Second item".to_string(),
 595//             data: None,
 596//             variables_reference: None,
 597//             source: None,
 598//             line: None,
 599//             column: None,
 600//             group: Some(dap::OutputEventGroup::End),
 601//             location_reference: None,
 602//         }))
 603//         .await;
 604
 605//     cx.run_until_parked();
 606
 607//     active_debug_session_panel(workspace, cx).update(cx, |debug_panel_item, cx| {
 608//         debug_panel_item
 609//             .mode()
 610//             .as_running()
 611//             .unwrap()
 612//             .update(cx, |running_state, cx| {
 613//                 running_state.console().update(cx, |console, cx| {
 614//                     console.editor().update(cx, |editor, cx| {
 615//                         pretty_assertions::assert_eq!(
 616//                             "
 617//                         First line
 618//                         First group
 619//                             First item in group 1
 620//                             Second item in group 1
 621//                             Second group
 622//                                 First item in group 2
 623//                                 Second item in group 2
 624//                             End group 2
 625//                         ⋯    End group 3
 626//                             Third item in group 1
 627//                         Second item
 628//                     "
 629//                             .unindent(),
 630//                             editor.display_text(cx)
 631//                         );
 632//                     })
 633//                 });
 634//             });
 635//     });
 636
 637//     let shutdown_session = project.update(cx, |project, cx| {
 638//         project.dap_store().update(cx, |dap_store, cx| {
 639//             dap_store.shutdown_session(session.read(cx).session_id(), cx)
 640//         })
 641//     });
 642
 643//     shutdown_session.await.unwrap();
 644// }
 645
 646// todo(debugger): enable this again
 647// #[gpui::test]
 648// async fn test_evaluate_expression(executor: BackgroundExecutor, cx: &mut TestAppContext) {
 649//     init_test(cx);
 650
 651//     const NEW_VALUE: &str = "{nested1: \"Nested 1 updated\", nested2: \"Nested 2 updated\"}";
 652
 653//     let called_evaluate = Arc::new(AtomicBool::new(false));
 654
 655//     let fs = FakeFs::new(executor.clone());
 656
 657//     let test_file_content = r#"
 658//         const variable1 = {
 659//             nested1: "Nested 1",
 660//             nested2: "Nested 2",
 661//         };
 662//         const variable2 = "Value 2";
 663//         const variable3 = "Value 3";
 664//     "#
 665//     .unindent();
 666
 667//     fs.insert_tree(
 668//         "/project",
 669//         json!({
 670//            "src": {
 671//                "test.js": test_file_content,
 672//            }
 673//         }),
 674//     )
 675//     .await;
 676
 677//     let project = Project::test(fs, ["/project".as_ref()], cx).await;
 678//     let workspace = init_test_workspace(&project, cx).await;
 679//     let cx = &mut VisualTestContext::from_window(*workspace, cx);
 680
 681//     let task = project.update(cx, |project, cx| {
 682//         project.start_debug_session(dap::test_config(None), cx)
 683//     });
 684
 685//     let session = task.await.unwrap();
 686//     let client = session.update(cx, |session, _| session.adapter_client().unwrap());
 687
 688//     client
 689//         .on_request::<Threads, _>(move |_, _| {
 690//             Ok(dap::ThreadsResponse {
 691//                 threads: vec![dap::Thread {
 692//                     id: 1,
 693//                     name: "Thread 1".into(),
 694//                 }],
 695//             })
 696//         })
 697//         .await;
 698
 699//     let stack_frames = vec![StackFrame {
 700//         id: 1,
 701//         name: "Stack Frame 1".into(),
 702//         source: Some(dap::Source {
 703//             name: Some("test.js".into()),
 704//             path: Some("/project/src/test.js".into()),
 705//             source_reference: None,
 706//             presentation_hint: None,
 707//             origin: None,
 708//             sources: None,
 709//             adapter_data: None,
 710//             checksums: None,
 711//         }),
 712//         line: 3,
 713//         column: 1,
 714//         end_line: None,
 715//         end_column: None,
 716//         can_restart: None,
 717//         instruction_pointer_reference: None,
 718//         module_id: None,
 719//         presentation_hint: None,
 720//     }];
 721
 722//     client
 723//         .on_request::<StackTrace, _>({
 724//             let stack_frames = Arc::new(stack_frames.clone());
 725//             move |_, args| {
 726//                 assert_eq!(1, args.thread_id);
 727
 728//                 Ok(dap::StackTraceResponse {
 729//                     stack_frames: (*stack_frames).clone(),
 730//                     total_frames: None,
 731//                 })
 732//             }
 733//         })
 734//         .await;
 735
 736//     let scopes = vec![
 737//         Scope {
 738//             name: "Scope 1".into(),
 739//             presentation_hint: None,
 740//             variables_reference: 2,
 741//             named_variables: None,
 742//             indexed_variables: None,
 743//             expensive: false,
 744//             source: None,
 745//             line: None,
 746//             column: None,
 747//             end_line: None,
 748//             end_column: None,
 749//         },
 750//         Scope {
 751//             name: "Scope 2".into(),
 752//             presentation_hint: None,
 753//             variables_reference: 4,
 754//             named_variables: None,
 755//             indexed_variables: None,
 756//             expensive: false,
 757//             source: None,
 758//             line: None,
 759//             column: None,
 760//             end_line: None,
 761//             end_column: None,
 762//         },
 763//     ];
 764
 765//     client
 766//         .on_request::<Scopes, _>({
 767//             let scopes = Arc::new(scopes.clone());
 768//             move |_, args| {
 769//                 assert_eq!(1, args.frame_id);
 770
 771//                 Ok(dap::ScopesResponse {
 772//                     scopes: (*scopes).clone(),
 773//                 })
 774//             }
 775//         })
 776//         .await;
 777
 778//     let scope1_variables = Arc::new(Mutex::new(vec![
 779//         Variable {
 780//             name: "variable1".into(),
 781//             value: "{nested1: \"Nested 1\", nested2: \"Nested 2\"}".into(),
 782//             type_: None,
 783//             presentation_hint: None,
 784//             evaluate_name: None,
 785//             variables_reference: 3,
 786//             named_variables: None,
 787//             indexed_variables: None,
 788//             memory_reference: None,
 789//             declaration_location_reference: None,
 790//             value_location_reference: None,
 791//         },
 792//         Variable {
 793//             name: "variable2".into(),
 794//             value: "Value 2".into(),
 795//             type_: None,
 796//             presentation_hint: None,
 797//             evaluate_name: None,
 798//             variables_reference: 0,
 799//             named_variables: None,
 800//             indexed_variables: None,
 801//             memory_reference: None,
 802//             declaration_location_reference: None,
 803//             value_location_reference: None,
 804//         },
 805//     ]));
 806
 807//     let nested_variables = vec![
 808//         Variable {
 809//             name: "nested1".into(),
 810//             value: "Nested 1".into(),
 811//             type_: None,
 812//             presentation_hint: None,
 813//             evaluate_name: None,
 814//             variables_reference: 0,
 815//             named_variables: None,
 816//             indexed_variables: None,
 817//             memory_reference: None,
 818//             declaration_location_reference: None,
 819//             value_location_reference: None,
 820//         },
 821//         Variable {
 822//             name: "nested2".into(),
 823//             value: "Nested 2".into(),
 824//             type_: None,
 825//             presentation_hint: None,
 826//             evaluate_name: None,
 827//             variables_reference: 0,
 828//             named_variables: None,
 829//             indexed_variables: None,
 830//             memory_reference: None,
 831//             declaration_location_reference: None,
 832//             value_location_reference: None,
 833//         },
 834//     ];
 835
 836//     let scope2_variables = vec![Variable {
 837//         name: "variable3".into(),
 838//         value: "Value 3".into(),
 839//         type_: None,
 840//         presentation_hint: None,
 841//         evaluate_name: None,
 842//         variables_reference: 0,
 843//         named_variables: None,
 844//         indexed_variables: None,
 845//         memory_reference: None,
 846//         declaration_location_reference: None,
 847//         value_location_reference: None,
 848//     }];
 849
 850//     client
 851//         .on_request::<Variables, _>({
 852//             let scope1_variables = scope1_variables.clone();
 853//             let nested_variables = Arc::new(nested_variables.clone());
 854//             let scope2_variables = Arc::new(scope2_variables.clone());
 855//             move |_, args| match args.variables_reference {
 856//                 4 => Ok(dap::VariablesResponse {
 857//                     variables: (*scope2_variables).clone(),
 858//                 }),
 859//                 3 => Ok(dap::VariablesResponse {
 860//                     variables: (*nested_variables).clone(),
 861//                 }),
 862//                 2 => Ok(dap::VariablesResponse {
 863//                     variables: scope1_variables.lock().unwrap().clone(),
 864//                 }),
 865//                 id => unreachable!("unexpected variables reference {id}"),
 866//             }
 867//         })
 868//         .await;
 869
 870//     client
 871//         .on_request::<Evaluate, _>({
 872//             let called_evaluate = called_evaluate.clone();
 873//             let scope1_variables = scope1_variables.clone();
 874//             move |_, args| {
 875//                 called_evaluate.store(true, Ordering::SeqCst);
 876
 877//                 assert_eq!(format!("$variable1 = {}", NEW_VALUE), args.expression);
 878//                 assert_eq!(Some(1), args.frame_id);
 879//                 assert_eq!(Some(dap::EvaluateArgumentsContext::Variables), args.context);
 880
 881//                 scope1_variables.lock().unwrap()[0].value = NEW_VALUE.to_string();
 882
 883//                 Ok(dap::EvaluateResponse {
 884//                     result: NEW_VALUE.into(),
 885//                     type_: None,
 886//                     presentation_hint: None,
 887//                     variables_reference: 0,
 888//                     named_variables: None,
 889//                     indexed_variables: None,
 890//                     memory_reference: None,
 891//                     value_location_reference: None,
 892//                 })
 893//             }
 894//         })
 895//         .await;
 896
 897//     client
 898//         .fake_event(dap::messages::Events::Stopped(dap::StoppedEvent {
 899//             reason: dap::StoppedEventReason::Pause,
 900//             description: None,
 901//             thread_id: Some(1),
 902//             preserve_focus_hint: None,
 903//             text: None,
 904//             all_threads_stopped: None,
 905//             hit_breakpoint_ids: None,
 906//         }))
 907//         .await;
 908
 909//     cx.run_until_parked();
 910
 911//     // toggle nested variables for scope 1
 912//     active_debug_session_panel(workspace, cx).update(cx, |debug_panel_item, cx| {
 913//         debug_panel_item
 914//             .mode()
 915//             .as_running()
 916//             .unwrap()
 917//             .update(cx, |running_state, cx| {
 918//                 running_state
 919//                     .variable_list()
 920//                     .update(cx, |variable_list, cx| {
 921//                         variable_list.toggle_variable(
 922//                             &VariablePath {
 923//                                 indices: Arc::from([scopes[0].variables_reference]),
 924//                             },
 925//                             cx,
 926//                         );
 927//                     });
 928//             });
 929//     });
 930
 931//     cx.run_until_parked();
 932
 933//     active_debug_session_panel(workspace, cx).update_in(cx, |debug_panel_item, window, cx| {
 934//         debug_panel_item
 935//             .mode()
 936//             .as_running()
 937//             .unwrap()
 938//             .update(cx, |running_state, cx| {
 939//                 running_state.console().update(cx, |console, item_cx| {
 940//                     console
 941//                         .query_bar()
 942//                         .update(item_cx, |query_bar, console_cx| {
 943//                             query_bar.set_text(
 944//                                 format!("$variable1 = {}", NEW_VALUE),
 945//                                 window,
 946//                                 console_cx,
 947//                             );
 948//                         });
 949
 950//                     console.evaluate(&menu::Confirm, window, item_cx);
 951//                 });
 952//             });
 953//     });
 954
 955//     cx.run_until_parked();
 956
 957//     active_debug_session_panel(workspace, cx).update(cx, |debug_panel_item, cx| {
 958//         assert_eq!(
 959//             "",
 960//             debug_panel_item
 961//                 .mode()
 962//                 .as_running()
 963//                 .unwrap()
 964//                 .read(cx)
 965//                 .console()
 966//                 .read(cx)
 967//                 .query_bar()
 968//                 .read(cx)
 969//                 .text(cx)
 970//                 .as_str()
 971//         );
 972
 973//         assert_eq!(
 974//             format!("{}\n", NEW_VALUE),
 975//             debug_panel_item
 976//                 .mode()
 977//                 .as_running()
 978//                 .unwrap()
 979//                 .read(cx)
 980//                 .console()
 981//                 .read(cx)
 982//                 .editor()
 983//                 .read(cx)
 984//                 .text(cx)
 985//                 .as_str()
 986//         );
 987
 988//         debug_panel_item
 989//             .mode()
 990//             .as_running()
 991//             .unwrap()
 992//             .update(cx, |running_state, cx| {
 993//                 running_state
 994//                     .variable_list()
 995//                     .update(cx, |variable_list, _| {
 996//                         let scope1_variables = scope1_variables.lock().unwrap().clone();
 997
 998//                         // scope 1
 999//                         // assert_eq!(
1000//                         //     vec![
1001//                         //         VariableContainer {
1002//                         //             container_reference: scopes[0].variables_reference,
1003//                         //             variable: scope1_variables[0].clone(),
1004//                         //             depth: 1,
1005//                         //         },
1006//                         //         VariableContainer {
1007//                         //             container_reference: scope1_variables[0].variables_reference,
1008//                         //             variable: nested_variables[0].clone(),
1009//                         //             depth: 2,
1010//                         //         },
1011//                         //         VariableContainer {
1012//                         //             container_reference: scope1_variables[0].variables_reference,
1013//                         //             variable: nested_variables[1].clone(),
1014//                         //             depth: 2,
1015//                         //         },
1016//                         //         VariableContainer {
1017//                         //             container_reference: scopes[0].variables_reference,
1018//                         //             variable: scope1_variables[1].clone(),
1019//                         //             depth: 1,
1020//                         //         },
1021//                         //     ],
1022//                         //     variable_list.variables_by_scope(1, 2).unwrap().variables()
1023//                         // );
1024
1025//                         // scope 2
1026//                         // assert_eq!(
1027//                         //     vec![VariableContainer {
1028//                         //         container_reference: scopes[1].variables_reference,
1029//                         //         variable: scope2_variables[0].clone(),
1030//                         //         depth: 1,
1031//                         //     }],
1032//                         //     variable_list.variables_by_scope(1, 4).unwrap().variables()
1033//                         // );
1034
1035//                         variable_list.assert_visual_entries(vec![
1036//                             "v Scope 1",
1037//                             "    v variable1",
1038//                             "       > nested1",
1039//                             "       > nested2",
1040//                             "    > variable2",
1041//                         ]);
1042
1043//                         // assert visual entries
1044//                         // assert_eq!(
1045//                         //     vec![
1046//                         //         VariableListEntry::Scope(scopes[0].clone()),
1047//                         //         VariableListEntry::Variable {
1048//                         //             depth: 1,
1049//                         //             scope: Arc::new(scopes[0].clone()),
1050//                         //             has_children: true,
1051//                         //             variable: Arc::new(scope1_variables[0].clone()),
1052//                         //             container_reference: scopes[0].variables_reference,
1053//                         //         },
1054//                         //         VariableListEntry::Variable {
1055//                         //             depth: 2,
1056//                         //             scope: Arc::new(scopes[0].clone()),
1057//                         //             has_children: false,
1058//                         //             variable: Arc::new(nested_variables[0].clone()),
1059//                         //             container_reference: scope1_variables[0].variables_reference,
1060//                         //         },
1061//                         //         VariableListEntry::Variable {
1062//                         //             depth: 2,
1063//                         //             scope: Arc::new(scopes[0].clone()),
1064//                         //             has_children: false,
1065//                         //             variable: Arc::new(nested_variables[1].clone()),
1066//                         //             container_reference: scope1_variables[0].variables_reference,
1067//                         //         },
1068//                         //         VariableListEntry::Variable {
1069//                         //             depth: 1,
1070//                         //             scope: Arc::new(scopes[0].clone()),
1071//                         //             has_children: false,
1072//                         //             variable: Arc::new(scope1_variables[1].clone()),
1073//                         //             container_reference: scopes[0].variables_reference,
1074//                         //         },
1075//                         //         VariableListEntry::Scope(scopes[1].clone()),
1076//                         //     ],
1077//                         //     variable_list.entries().get(&1).unwrap().clone()
1078//                         // );
1079//                     });
1080//             });
1081//     });
1082
1083//     assert!(
1084//         called_evaluate.load(std::sync::atomic::Ordering::SeqCst),
1085//         "Expected evaluate request to be called"
1086//     );
1087
1088//     let shutdown_session = project.update(cx, |project, cx| {
1089//         project.dap_store().update(cx, |dap_store, cx| {
1090//             dap_store.shutdown_session(&session.read(cx).session_id(), cx)
1091//         })
1092//     });
1093
1094//     shutdown_session.await.unwrap();
1095// }