variable_list.rs

   1use std::sync::{
   2    atomic::{AtomicBool, Ordering},
   3    Arc,
   4};
   5
   6use crate::{
   7    session::running::variable_list::{CollapseSelectedEntry, ExpandSelectedEntry},
   8    tests::{active_debug_session_panel, init_test, init_test_workspace},
   9    DebugPanel,
  10};
  11use collections::HashMap;
  12use dap::{
  13    requests::{Initialize, Launch, Scopes, StackTrace, Variables},
  14    Scope, StackFrame, Variable,
  15};
  16use gpui::{BackgroundExecutor, TestAppContext, VisualTestContext};
  17use menu::{SelectFirst, SelectNext, SelectPrevious};
  18use project::{FakeFs, Project};
  19use serde_json::json;
  20use unindent::Unindent as _;
  21use util::path;
  22
  23/// This only tests fetching one scope and 2 variables for a single stackframe
  24#[gpui::test]
  25async fn test_basic_fetch_initial_scope_and_variables(
  26    executor: BackgroundExecutor,
  27    cx: &mut TestAppContext,
  28) {
  29    init_test(cx);
  30
  31    let fs = FakeFs::new(executor.clone());
  32
  33    let test_file_content = r#"
  34        const variable1 = "Value 1";
  35        const variable2 = "Value 2";
  36    "#
  37    .unindent();
  38
  39    fs.insert_tree(
  40        path!("/project"),
  41        json!({
  42           "src": {
  43               "test.js": test_file_content,
  44           }
  45        }),
  46    )
  47    .await;
  48
  49    let project = Project::test(fs, [path!("/project").as_ref()], cx).await;
  50    let workspace = init_test_workspace(&project, cx).await;
  51    workspace
  52        .update(cx, |workspace, window, cx| {
  53            workspace.focus_panel::<DebugPanel>(window, cx);
  54        })
  55        .unwrap();
  56    let cx = &mut VisualTestContext::from_window(*workspace, cx);
  57
  58    let task = project.update(cx, |project, cx| {
  59        project.start_debug_session(
  60            dap::test_config(dap::DebugRequestType::Launch, None, None),
  61            cx,
  62        )
  63    });
  64
  65    let session = task.await.unwrap();
  66    let client = session.update(cx, |session, _| session.adapter_client().unwrap());
  67
  68    client
  69        .on_request::<dap::requests::Threads, _>(move |_, _| {
  70            Ok(dap::ThreadsResponse {
  71                threads: vec![dap::Thread {
  72                    id: 1,
  73                    name: "Thread 1".into(),
  74                }],
  75            })
  76        })
  77        .await;
  78
  79    let stack_frames = vec![StackFrame {
  80        id: 1,
  81        name: "Stack Frame 1".into(),
  82        source: Some(dap::Source {
  83            name: Some("test.js".into()),
  84            path: Some(path!("/project/src/test.js").into()),
  85            source_reference: None,
  86            presentation_hint: None,
  87            origin: None,
  88            sources: None,
  89            adapter_data: None,
  90            checksums: None,
  91        }),
  92        line: 1,
  93        column: 1,
  94        end_line: None,
  95        end_column: None,
  96        can_restart: None,
  97        instruction_pointer_reference: None,
  98        module_id: None,
  99        presentation_hint: None,
 100    }];
 101
 102    client
 103        .on_request::<StackTrace, _>({
 104            let stack_frames = Arc::new(stack_frames.clone());
 105            move |_, args| {
 106                assert_eq!(1, args.thread_id);
 107
 108                Ok(dap::StackTraceResponse {
 109                    stack_frames: (*stack_frames).clone(),
 110                    total_frames: None,
 111                })
 112            }
 113        })
 114        .await;
 115
 116    let scopes = vec![Scope {
 117        name: "Scope 1".into(),
 118        presentation_hint: None,
 119        variables_reference: 2,
 120        named_variables: None,
 121        indexed_variables: None,
 122        expensive: false,
 123        source: None,
 124        line: None,
 125        column: None,
 126        end_line: None,
 127        end_column: None,
 128    }];
 129
 130    client
 131        .on_request::<Scopes, _>({
 132            let scopes = Arc::new(scopes.clone());
 133            move |_, args| {
 134                assert_eq!(1, args.frame_id);
 135
 136                Ok(dap::ScopesResponse {
 137                    scopes: (*scopes).clone(),
 138                })
 139            }
 140        })
 141        .await;
 142
 143    let variables = vec![
 144        Variable {
 145            name: "variable1".into(),
 146            value: "value 1".into(),
 147            type_: None,
 148            presentation_hint: None,
 149            evaluate_name: None,
 150            variables_reference: 0,
 151            named_variables: None,
 152            indexed_variables: None,
 153            memory_reference: None,
 154            declaration_location_reference: None,
 155            value_location_reference: None,
 156        },
 157        Variable {
 158            name: "variable2".into(),
 159            value: "value 2".into(),
 160            type_: None,
 161            presentation_hint: None,
 162            evaluate_name: None,
 163            variables_reference: 0,
 164            named_variables: None,
 165            indexed_variables: None,
 166            memory_reference: None,
 167            declaration_location_reference: None,
 168            value_location_reference: None,
 169        },
 170    ];
 171
 172    client
 173        .on_request::<Variables, _>({
 174            let variables = Arc::new(variables.clone());
 175            move |_, args| {
 176                assert_eq!(2, args.variables_reference);
 177
 178                Ok(dap::VariablesResponse {
 179                    variables: (*variables).clone(),
 180                })
 181            }
 182        })
 183        .await;
 184
 185    client
 186        .fake_event(dap::messages::Events::Stopped(dap::StoppedEvent {
 187            reason: dap::StoppedEventReason::Pause,
 188            description: None,
 189            thread_id: Some(1),
 190            preserve_focus_hint: None,
 191            text: None,
 192            all_threads_stopped: None,
 193            hit_breakpoint_ids: None,
 194        }))
 195        .await;
 196
 197    cx.run_until_parked();
 198
 199    let running_state =
 200        active_debug_session_panel(workspace, cx).update_in(cx, |item, window, cx| {
 201            cx.focus_self(window);
 202            item.mode()
 203                .as_running()
 204                .expect("Session should be running by this point")
 205                .clone()
 206        });
 207
 208    cx.run_until_parked();
 209
 210    running_state.update(cx, |running_state, cx| {
 211        let (stack_frame_list, stack_frame_id) =
 212            running_state.stack_frame_list().update(cx, |list, _| {
 213                (list.flatten_entries(), list.current_stack_frame_id())
 214            });
 215
 216        assert_eq!(stack_frames, stack_frame_list);
 217        assert_eq!(Some(1), stack_frame_id);
 218
 219        running_state
 220            .variable_list()
 221            .update(cx, |variable_list, _| {
 222                assert_eq!(1, variable_list.scopes().len());
 223                assert_eq!(scopes, variable_list.scopes());
 224                assert_eq!(
 225                    vec![variables[0].clone(), variables[1].clone(),],
 226                    variable_list.variables()
 227                );
 228
 229                variable_list.assert_visual_entries(vec![
 230                    "v Scope 1",
 231                    "    > variable1",
 232                    "    > variable2",
 233                ]);
 234            });
 235    });
 236
 237    let shutdown_session = project.update(cx, |project, cx| {
 238        project.dap_store().update(cx, |dap_store, cx| {
 239            dap_store.shutdown_session(session.read(cx).session_id(), cx)
 240        })
 241    });
 242
 243    shutdown_session.await.unwrap();
 244}
 245
 246/// This tests fetching multiple scopes and variables for them with a single stackframe
 247#[gpui::test]
 248async fn test_fetch_variables_for_multiple_scopes(
 249    executor: BackgroundExecutor,
 250    cx: &mut TestAppContext,
 251) {
 252    init_test(cx);
 253
 254    let fs = FakeFs::new(executor.clone());
 255
 256    let test_file_content = r#"
 257        const variable1 = {
 258            nested1: "Nested 1",
 259            nested2: "Nested 2",
 260        };
 261        const variable2 = "Value 2";
 262        const variable3 = "Value 3";
 263    "#
 264    .unindent();
 265
 266    fs.insert_tree(
 267        path!("/project"),
 268        json!({
 269           "src": {
 270               "test.js": test_file_content,
 271           }
 272        }),
 273    )
 274    .await;
 275
 276    let project = Project::test(fs, [path!("/project").as_ref()], cx).await;
 277    let workspace = init_test_workspace(&project, cx).await;
 278    workspace
 279        .update(cx, |workspace, window, cx| {
 280            workspace.focus_panel::<DebugPanel>(window, cx);
 281        })
 282        .unwrap();
 283    let cx = &mut VisualTestContext::from_window(*workspace, cx);
 284
 285    let task = project.update(cx, |project, cx| {
 286        project.start_debug_session(
 287            dap::test_config(dap::DebugRequestType::Launch, None, None),
 288            cx,
 289        )
 290    });
 291
 292    let session = task.await.unwrap();
 293    let client = session.update(cx, |session, _| session.adapter_client().unwrap());
 294
 295    client
 296        .on_request::<dap::requests::Threads, _>(move |_, _| {
 297            Ok(dap::ThreadsResponse {
 298                threads: vec![dap::Thread {
 299                    id: 1,
 300                    name: "Thread 1".into(),
 301                }],
 302            })
 303        })
 304        .await;
 305
 306    client
 307        .on_request::<Initialize, _>(move |_, _| {
 308            Ok(dap::Capabilities {
 309                supports_step_back: Some(false),
 310                ..Default::default()
 311            })
 312        })
 313        .await;
 314
 315    client.on_request::<Launch, _>(move |_, _| Ok(())).await;
 316
 317    let stack_frames = vec![StackFrame {
 318        id: 1,
 319        name: "Stack Frame 1".into(),
 320        source: Some(dap::Source {
 321            name: Some("test.js".into()),
 322            path: Some(path!("/project/src/test.js").into()),
 323            source_reference: None,
 324            presentation_hint: None,
 325            origin: None,
 326            sources: None,
 327            adapter_data: None,
 328            checksums: None,
 329        }),
 330        line: 1,
 331        column: 1,
 332        end_line: None,
 333        end_column: None,
 334        can_restart: None,
 335        instruction_pointer_reference: None,
 336        module_id: None,
 337        presentation_hint: None,
 338    }];
 339
 340    client
 341        .on_request::<StackTrace, _>({
 342            let stack_frames = Arc::new(stack_frames.clone());
 343            move |_, args| {
 344                assert_eq!(1, args.thread_id);
 345
 346                Ok(dap::StackTraceResponse {
 347                    stack_frames: (*stack_frames).clone(),
 348                    total_frames: None,
 349                })
 350            }
 351        })
 352        .await;
 353
 354    let scopes = vec![
 355        Scope {
 356            name: "Scope 1".into(),
 357            presentation_hint: Some(dap::ScopePresentationHint::Locals),
 358            variables_reference: 2,
 359            named_variables: None,
 360            indexed_variables: None,
 361            expensive: false,
 362            source: None,
 363            line: None,
 364            column: None,
 365            end_line: None,
 366            end_column: None,
 367        },
 368        Scope {
 369            name: "Scope 2".into(),
 370            presentation_hint: None,
 371            variables_reference: 3,
 372            named_variables: None,
 373            indexed_variables: None,
 374            expensive: false,
 375            source: None,
 376            line: None,
 377            column: None,
 378            end_line: None,
 379            end_column: None,
 380        },
 381    ];
 382
 383    client
 384        .on_request::<Scopes, _>({
 385            let scopes = Arc::new(scopes.clone());
 386            move |_, args| {
 387                assert_eq!(1, args.frame_id);
 388
 389                Ok(dap::ScopesResponse {
 390                    scopes: (*scopes).clone(),
 391                })
 392            }
 393        })
 394        .await;
 395
 396    let mut variables = HashMap::default();
 397    variables.insert(
 398        2,
 399        vec![
 400            Variable {
 401                name: "variable1".into(),
 402                value: "{nested1: \"Nested 1\", nested2: \"Nested 2\"}".into(),
 403                type_: None,
 404                presentation_hint: None,
 405                evaluate_name: None,
 406                variables_reference: 0,
 407                named_variables: None,
 408                indexed_variables: None,
 409                memory_reference: None,
 410                declaration_location_reference: None,
 411                value_location_reference: None,
 412            },
 413            Variable {
 414                name: "variable2".into(),
 415                value: "Value 2".into(),
 416                type_: None,
 417                presentation_hint: None,
 418                evaluate_name: None,
 419                variables_reference: 0,
 420                named_variables: None,
 421                indexed_variables: None,
 422                memory_reference: None,
 423                declaration_location_reference: None,
 424                value_location_reference: None,
 425            },
 426        ],
 427    );
 428    variables.insert(
 429        3,
 430        vec![Variable {
 431            name: "variable3".into(),
 432            value: "Value 3".into(),
 433            type_: None,
 434            presentation_hint: None,
 435            evaluate_name: None,
 436            variables_reference: 0,
 437            named_variables: None,
 438            indexed_variables: None,
 439            memory_reference: None,
 440            declaration_location_reference: None,
 441            value_location_reference: None,
 442        }],
 443    );
 444
 445    client
 446        .on_request::<Variables, _>({
 447            let variables = Arc::new(variables.clone());
 448            move |_, args| {
 449                Ok(dap::VariablesResponse {
 450                    variables: variables.get(&args.variables_reference).unwrap().clone(),
 451                })
 452            }
 453        })
 454        .await;
 455
 456    client
 457        .fake_event(dap::messages::Events::Stopped(dap::StoppedEvent {
 458            reason: dap::StoppedEventReason::Pause,
 459            description: None,
 460            thread_id: Some(1),
 461            preserve_focus_hint: None,
 462            text: None,
 463            all_threads_stopped: None,
 464            hit_breakpoint_ids: None,
 465        }))
 466        .await;
 467
 468    cx.run_until_parked();
 469
 470    let running_state =
 471        active_debug_session_panel(workspace, cx).update_in(cx, |item, window, cx| {
 472            cx.focus_self(window);
 473            item.mode()
 474                .as_running()
 475                .expect("Session should be running by this point")
 476                .clone()
 477        });
 478
 479    cx.run_until_parked();
 480
 481    running_state.update(cx, |running_state, cx| {
 482        let (stack_frame_list, stack_frame_id) =
 483            running_state.stack_frame_list().update(cx, |list, _| {
 484                (list.flatten_entries(), list.current_stack_frame_id())
 485            });
 486
 487        assert_eq!(Some(1), stack_frame_id);
 488        assert_eq!(stack_frames, stack_frame_list);
 489
 490        running_state
 491            .variable_list()
 492            .update(cx, |variable_list, _| {
 493                assert_eq!(2, variable_list.scopes().len());
 494                assert_eq!(scopes, variable_list.scopes());
 495                let variables_by_scope = variable_list.variables_per_scope();
 496
 497                // scope 1
 498                assert_eq!(
 499                    vec![
 500                        variables.get(&2).unwrap()[0].clone(),
 501                        variables.get(&2).unwrap()[1].clone(),
 502                    ],
 503                    variables_by_scope[0].1
 504                );
 505
 506                // scope 2
 507                let empty_vec: Vec<dap::Variable> = vec![];
 508                assert_eq!(empty_vec, variables_by_scope[1].1);
 509
 510                variable_list.assert_visual_entries(vec![
 511                    "v Scope 1",
 512                    "    > variable1",
 513                    "    > variable2",
 514                    "> Scope 2",
 515                ]);
 516            });
 517    });
 518
 519    let shutdown_session = project.update(cx, |project, cx| {
 520        project.dap_store().update(cx, |dap_store, cx| {
 521            dap_store.shutdown_session(session.read(cx).session_id(), cx)
 522        })
 523    });
 524
 525    shutdown_session.await.unwrap();
 526}
 527
 528// tests that toggling a variable will fetch its children and shows it
 529#[gpui::test]
 530async fn test_keyboard_navigation(executor: BackgroundExecutor, cx: &mut TestAppContext) {
 531    init_test(cx);
 532
 533    let fs = FakeFs::new(executor.clone());
 534
 535    let test_file_content = r#"
 536        const variable1 = {
 537            nested1: "Nested 1",
 538            nested2: "Nested 2",
 539        };
 540        const variable2 = "Value 2";
 541        const variable3 = "Value 3";
 542    "#
 543    .unindent();
 544
 545    fs.insert_tree(
 546        path!("/project"),
 547        json!({
 548           "src": {
 549               "test.js": test_file_content,
 550           }
 551        }),
 552    )
 553    .await;
 554
 555    let project = Project::test(fs, [path!("/project").as_ref()], cx).await;
 556    let workspace = init_test_workspace(&project, cx).await;
 557    workspace
 558        .update(cx, |workspace, window, cx| {
 559            workspace.focus_panel::<DebugPanel>(window, cx);
 560        })
 561        .unwrap();
 562    let cx = &mut VisualTestContext::from_window(*workspace, cx);
 563
 564    let task = project.update(cx, |project, cx| {
 565        project.start_debug_session(
 566            dap::test_config(dap::DebugRequestType::Launch, None, None),
 567            cx,
 568        )
 569    });
 570
 571    let session = task.await.unwrap();
 572    let client = session.update(cx, |session, _| session.adapter_client().unwrap());
 573
 574    client
 575        .on_request::<dap::requests::Threads, _>(move |_, _| {
 576            Ok(dap::ThreadsResponse {
 577                threads: vec![dap::Thread {
 578                    id: 1,
 579                    name: "Thread 1".into(),
 580                }],
 581            })
 582        })
 583        .await;
 584
 585    client
 586        .on_request::<Initialize, _>(move |_, _| {
 587            Ok(dap::Capabilities {
 588                supports_step_back: Some(false),
 589                ..Default::default()
 590            })
 591        })
 592        .await;
 593
 594    client.on_request::<Launch, _>(move |_, _| Ok(())).await;
 595
 596    let stack_frames = vec![StackFrame {
 597        id: 1,
 598        name: "Stack Frame 1".into(),
 599        source: Some(dap::Source {
 600            name: Some("test.js".into()),
 601            path: Some(path!("/project/src/test.js").into()),
 602            source_reference: None,
 603            presentation_hint: None,
 604            origin: None,
 605            sources: None,
 606            adapter_data: None,
 607            checksums: None,
 608        }),
 609        line: 1,
 610        column: 1,
 611        end_line: None,
 612        end_column: None,
 613        can_restart: None,
 614        instruction_pointer_reference: None,
 615        module_id: None,
 616        presentation_hint: None,
 617    }];
 618
 619    client
 620        .on_request::<StackTrace, _>({
 621            let stack_frames = Arc::new(stack_frames.clone());
 622            move |_, args| {
 623                assert_eq!(1, args.thread_id);
 624
 625                Ok(dap::StackTraceResponse {
 626                    stack_frames: (*stack_frames).clone(),
 627                    total_frames: None,
 628                })
 629            }
 630        })
 631        .await;
 632
 633    let scopes = vec![
 634        Scope {
 635            name: "Scope 1".into(),
 636            presentation_hint: Some(dap::ScopePresentationHint::Locals),
 637            variables_reference: 2,
 638            named_variables: None,
 639            indexed_variables: None,
 640            expensive: false,
 641            source: None,
 642            line: None,
 643            column: None,
 644            end_line: None,
 645            end_column: None,
 646        },
 647        Scope {
 648            name: "Scope 2".into(),
 649            presentation_hint: None,
 650            variables_reference: 4,
 651            named_variables: None,
 652            indexed_variables: None,
 653            expensive: false,
 654            source: None,
 655            line: None,
 656            column: None,
 657            end_line: None,
 658            end_column: None,
 659        },
 660    ];
 661
 662    client
 663        .on_request::<Scopes, _>({
 664            let scopes = Arc::new(scopes.clone());
 665            move |_, args| {
 666                assert_eq!(1, args.frame_id);
 667
 668                Ok(dap::ScopesResponse {
 669                    scopes: (*scopes).clone(),
 670                })
 671            }
 672        })
 673        .await;
 674
 675    let scope1_variables = vec![
 676        Variable {
 677            name: "variable1".into(),
 678            value: "{nested1: \"Nested 1\", nested2: \"Nested 2\"}".into(),
 679            type_: None,
 680            presentation_hint: None,
 681            evaluate_name: None,
 682            variables_reference: 3,
 683            named_variables: None,
 684            indexed_variables: None,
 685            memory_reference: None,
 686            declaration_location_reference: None,
 687            value_location_reference: None,
 688        },
 689        Variable {
 690            name: "variable2".into(),
 691            value: "Value 2".into(),
 692            type_: None,
 693            presentation_hint: None,
 694            evaluate_name: None,
 695            variables_reference: 0,
 696            named_variables: None,
 697            indexed_variables: None,
 698            memory_reference: None,
 699            declaration_location_reference: None,
 700            value_location_reference: None,
 701        },
 702    ];
 703
 704    let nested_variables = vec![
 705        Variable {
 706            name: "nested1".into(),
 707            value: "Nested 1".into(),
 708            type_: None,
 709            presentation_hint: None,
 710            evaluate_name: None,
 711            variables_reference: 0,
 712            named_variables: None,
 713            indexed_variables: None,
 714            memory_reference: None,
 715            declaration_location_reference: None,
 716            value_location_reference: None,
 717        },
 718        Variable {
 719            name: "nested2".into(),
 720            value: "Nested 2".into(),
 721            type_: None,
 722            presentation_hint: None,
 723            evaluate_name: None,
 724            variables_reference: 0,
 725            named_variables: None,
 726            indexed_variables: None,
 727            memory_reference: None,
 728            declaration_location_reference: None,
 729            value_location_reference: None,
 730        },
 731    ];
 732
 733    let scope2_variables = vec![Variable {
 734        name: "variable3".into(),
 735        value: "Value 3".into(),
 736        type_: None,
 737        presentation_hint: None,
 738        evaluate_name: None,
 739        variables_reference: 0,
 740        named_variables: None,
 741        indexed_variables: None,
 742        memory_reference: None,
 743        declaration_location_reference: None,
 744        value_location_reference: None,
 745    }];
 746
 747    client
 748        .on_request::<Variables, _>({
 749            let scope1_variables = Arc::new(scope1_variables.clone());
 750            let nested_variables = Arc::new(nested_variables.clone());
 751            let scope2_variables = Arc::new(scope2_variables.clone());
 752            move |_, args| match args.variables_reference {
 753                4 => Ok(dap::VariablesResponse {
 754                    variables: (*scope2_variables).clone(),
 755                }),
 756                3 => Ok(dap::VariablesResponse {
 757                    variables: (*nested_variables).clone(),
 758                }),
 759                2 => Ok(dap::VariablesResponse {
 760                    variables: (*scope1_variables).clone(),
 761                }),
 762                id => unreachable!("unexpected variables reference {id}"),
 763            }
 764        })
 765        .await;
 766
 767    client
 768        .fake_event(dap::messages::Events::Stopped(dap::StoppedEvent {
 769            reason: dap::StoppedEventReason::Pause,
 770            description: None,
 771            thread_id: Some(1),
 772            preserve_focus_hint: None,
 773            text: None,
 774            all_threads_stopped: None,
 775            hit_breakpoint_ids: None,
 776        }))
 777        .await;
 778
 779    cx.run_until_parked();
 780    let running_state =
 781        active_debug_session_panel(workspace, cx).update_in(cx, |item, window, cx| {
 782            cx.focus_self(window);
 783            let running = item
 784                .mode()
 785                .as_running()
 786                .expect("Session should be running by this point")
 787                .clone();
 788
 789            let variable_list = running.read_with(cx, |state, _| state.variable_list().clone());
 790            variable_list.update(cx, |_, cx| cx.focus_self(window));
 791            running
 792        });
 793
 794    cx.dispatch_action(SelectFirst);
 795    cx.run_until_parked();
 796
 797    running_state.update(cx, |running_state, cx| {
 798        running_state
 799            .variable_list()
 800            .update(cx, |variable_list, _| {
 801                variable_list.assert_visual_entries(vec![
 802                    "v Scope 1 <=== selected",
 803                    "    > variable1",
 804                    "    > variable2",
 805                    "> Scope 2",
 806                ]);
 807            });
 808    });
 809
 810    cx.dispatch_action(SelectNext);
 811    cx.run_until_parked();
 812
 813    running_state.update(cx, |running_state, cx| {
 814        running_state
 815            .variable_list()
 816            .update(cx, |variable_list, _| {
 817                variable_list.assert_visual_entries(vec![
 818                    "v Scope 1",
 819                    "    > variable1 <=== selected",
 820                    "    > variable2",
 821                    "> Scope 2",
 822                ]);
 823            });
 824    });
 825
 826    // expand the nested variables of variable 1
 827    cx.dispatch_action(ExpandSelectedEntry);
 828    cx.run_until_parked();
 829    running_state.update(cx, |running_state, cx| {
 830        running_state
 831            .variable_list()
 832            .update(cx, |variable_list, _| {
 833                variable_list.assert_visual_entries(vec![
 834                    "v Scope 1",
 835                    "    v variable1 <=== selected",
 836                    "        > nested1",
 837                    "        > nested2",
 838                    "    > variable2",
 839                    "> Scope 2",
 840                ]);
 841            });
 842    });
 843
 844    // select the first nested variable of variable 1
 845    cx.dispatch_action(SelectNext);
 846    cx.run_until_parked();
 847    running_state.update(cx, |debug_panel_item, cx| {
 848        debug_panel_item
 849            .variable_list()
 850            .update(cx, |variable_list, _| {
 851                variable_list.assert_visual_entries(vec![
 852                    "v Scope 1",
 853                    "    v variable1",
 854                    "        > nested1 <=== selected",
 855                    "        > nested2",
 856                    "    > variable2",
 857                    "> Scope 2",
 858                ]);
 859            });
 860    });
 861
 862    // select the second nested variable of variable 1
 863    cx.dispatch_action(SelectNext);
 864    cx.run_until_parked();
 865    running_state.update(cx, |debug_panel_item, cx| {
 866        debug_panel_item
 867            .variable_list()
 868            .update(cx, |variable_list, _| {
 869                variable_list.assert_visual_entries(vec![
 870                    "v Scope 1",
 871                    "    v variable1",
 872                    "        > nested1",
 873                    "        > nested2 <=== selected",
 874                    "    > variable2",
 875                    "> Scope 2",
 876                ]);
 877            });
 878    });
 879
 880    // select variable 2 of scope 1
 881    cx.dispatch_action(SelectNext);
 882    cx.run_until_parked();
 883    running_state.update(cx, |debug_panel_item, cx| {
 884        debug_panel_item
 885            .variable_list()
 886            .update(cx, |variable_list, _| {
 887                variable_list.assert_visual_entries(vec![
 888                    "v Scope 1",
 889                    "    v variable1",
 890                    "        > nested1",
 891                    "        > nested2",
 892                    "    > variable2 <=== selected",
 893                    "> Scope 2",
 894                ]);
 895            });
 896    });
 897
 898    // select scope 2
 899    cx.dispatch_action(SelectNext);
 900    cx.run_until_parked();
 901    running_state.update(cx, |debug_panel_item, cx| {
 902        debug_panel_item
 903            .variable_list()
 904            .update(cx, |variable_list, _| {
 905                variable_list.assert_visual_entries(vec![
 906                    "v Scope 1",
 907                    "    v variable1",
 908                    "        > nested1",
 909                    "        > nested2",
 910                    "    > variable2",
 911                    "> Scope 2 <=== selected",
 912                ]);
 913            });
 914    });
 915
 916    // expand the nested variables of scope 2
 917    cx.dispatch_action(ExpandSelectedEntry);
 918    cx.run_until_parked();
 919    running_state.update(cx, |debug_panel_item, cx| {
 920        debug_panel_item
 921            .variable_list()
 922            .update(cx, |variable_list, _| {
 923                variable_list.assert_visual_entries(vec![
 924                    "v Scope 1",
 925                    "    v variable1",
 926                    "        > nested1",
 927                    "        > nested2",
 928                    "    > variable2",
 929                    "v Scope 2 <=== selected",
 930                    "    > variable3",
 931                ]);
 932            });
 933    });
 934
 935    // select variable 3 of scope 2
 936    cx.dispatch_action(SelectNext);
 937    cx.run_until_parked();
 938    running_state.update(cx, |debug_panel_item, cx| {
 939        debug_panel_item
 940            .variable_list()
 941            .update(cx, |variable_list, _| {
 942                variable_list.assert_visual_entries(vec![
 943                    "v Scope 1",
 944                    "    v variable1",
 945                    "        > nested1",
 946                    "        > nested2",
 947                    "    > variable2",
 948                    "v Scope 2",
 949                    "    > variable3 <=== selected",
 950                ]);
 951            });
 952    });
 953
 954    // select scope 2
 955    cx.dispatch_action(SelectPrevious);
 956    cx.run_until_parked();
 957    running_state.update(cx, |debug_panel_item, cx| {
 958        debug_panel_item
 959            .variable_list()
 960            .update(cx, |variable_list, _| {
 961                variable_list.assert_visual_entries(vec![
 962                    "v Scope 1",
 963                    "    v variable1",
 964                    "        > nested1",
 965                    "        > nested2",
 966                    "    > variable2",
 967                    "v Scope 2 <=== selected",
 968                    "    > variable3",
 969                ]);
 970            });
 971    });
 972
 973    // collapse variables of scope 2
 974    cx.dispatch_action(CollapseSelectedEntry);
 975    cx.run_until_parked();
 976    running_state.update(cx, |debug_panel_item, cx| {
 977        debug_panel_item
 978            .variable_list()
 979            .update(cx, |variable_list, _| {
 980                variable_list.assert_visual_entries(vec![
 981                    "v Scope 1",
 982                    "    v variable1",
 983                    "        > nested1",
 984                    "        > nested2",
 985                    "    > variable2",
 986                    "> Scope 2 <=== selected",
 987                ]);
 988            });
 989    });
 990
 991    // select variable 2 of scope 1
 992    cx.dispatch_action(SelectPrevious);
 993    cx.run_until_parked();
 994    running_state.update(cx, |debug_panel_item, cx| {
 995        debug_panel_item
 996            .variable_list()
 997            .update(cx, |variable_list, _| {
 998                variable_list.assert_visual_entries(vec![
 999                    "v Scope 1",
1000                    "    v variable1",
1001                    "        > nested1",
1002                    "        > nested2",
1003                    "    > variable2 <=== selected",
1004                    "> Scope 2",
1005                ]);
1006            });
1007    });
1008
1009    // select nested2 of variable 1
1010    cx.dispatch_action(SelectPrevious);
1011    cx.run_until_parked();
1012    running_state.update(cx, |debug_panel_item, cx| {
1013        debug_panel_item
1014            .variable_list()
1015            .update(cx, |variable_list, _| {
1016                variable_list.assert_visual_entries(vec![
1017                    "v Scope 1",
1018                    "    v variable1",
1019                    "        > nested1",
1020                    "        > nested2 <=== selected",
1021                    "    > variable2",
1022                    "> Scope 2",
1023                ]);
1024            });
1025    });
1026
1027    // select nested1 of variable 1
1028    cx.dispatch_action(SelectPrevious);
1029    cx.run_until_parked();
1030    running_state.update(cx, |debug_panel_item, cx| {
1031        debug_panel_item
1032            .variable_list()
1033            .update(cx, |variable_list, _| {
1034                variable_list.assert_visual_entries(vec![
1035                    "v Scope 1",
1036                    "    v variable1",
1037                    "        > nested1 <=== selected",
1038                    "        > nested2",
1039                    "    > variable2",
1040                    "> Scope 2",
1041                ]);
1042            });
1043    });
1044
1045    // select variable 1 of scope 1
1046    cx.dispatch_action(SelectPrevious);
1047    cx.run_until_parked();
1048    running_state.update(cx, |debug_panel_item, cx| {
1049        debug_panel_item
1050            .variable_list()
1051            .update(cx, |variable_list, _| {
1052                variable_list.assert_visual_entries(vec![
1053                    "v Scope 1",
1054                    "    v variable1 <=== selected",
1055                    "        > nested1",
1056                    "        > nested2",
1057                    "    > variable2",
1058                    "> Scope 2",
1059                ]);
1060            });
1061    });
1062
1063    // collapse variables of variable 1
1064    cx.dispatch_action(CollapseSelectedEntry);
1065    cx.run_until_parked();
1066    running_state.update(cx, |debug_panel_item, cx| {
1067        debug_panel_item
1068            .variable_list()
1069            .update(cx, |variable_list, _| {
1070                variable_list.assert_visual_entries(vec![
1071                    "v Scope 1",
1072                    "    > variable1 <=== selected",
1073                    "    > variable2",
1074                    "> Scope 2",
1075                ]);
1076            });
1077    });
1078
1079    // select scope 1
1080    cx.dispatch_action(SelectPrevious);
1081    cx.run_until_parked();
1082    running_state.update(cx, |running_state, cx| {
1083        running_state
1084            .variable_list()
1085            .update(cx, |variable_list, _| {
1086                variable_list.assert_visual_entries(vec![
1087                    "v Scope 1 <=== selected",
1088                    "    > variable1",
1089                    "    > variable2",
1090                    "> Scope 2",
1091                ]);
1092            });
1093    });
1094
1095    // collapse variables of scope 1
1096    cx.dispatch_action(CollapseSelectedEntry);
1097    cx.run_until_parked();
1098    running_state.update(cx, |debug_panel_item, cx| {
1099        debug_panel_item
1100            .variable_list()
1101            .update(cx, |variable_list, _| {
1102                variable_list.assert_visual_entries(vec!["> Scope 1 <=== selected", "> Scope 2"]);
1103            });
1104    });
1105
1106    let shutdown_session = project.update(cx, |project, cx| {
1107        project.dap_store().update(cx, |dap_store, cx| {
1108            dap_store.shutdown_session(session.read(cx).session_id(), cx)
1109        })
1110    });
1111
1112    shutdown_session.await.unwrap();
1113}
1114
1115#[gpui::test]
1116async fn test_variable_list_only_sends_requests_when_rendering(
1117    executor: BackgroundExecutor,
1118    cx: &mut TestAppContext,
1119) {
1120    init_test(cx);
1121
1122    let fs = FakeFs::new(executor.clone());
1123
1124    let test_file_content = r#"
1125        import { SOME_VALUE } './module.js';
1126
1127        console.log(SOME_VALUE);
1128    "#
1129    .unindent();
1130
1131    let module_file_content = r#"
1132        export SOME_VALUE = 'some value';
1133    "#
1134    .unindent();
1135
1136    fs.insert_tree(
1137        path!("/project"),
1138        json!({
1139           "src": {
1140               "test.js": test_file_content,
1141               "module.js": module_file_content,
1142           }
1143        }),
1144    )
1145    .await;
1146
1147    let project = Project::test(fs, [path!("/project").as_ref()], cx).await;
1148    let workspace = init_test_workspace(&project, cx).await;
1149    let cx = &mut VisualTestContext::from_window(*workspace, cx);
1150
1151    let task = project.update(cx, |project, cx| {
1152        project.start_debug_session(
1153            dap::test_config(dap::DebugRequestType::Launch, None, None),
1154            cx,
1155        )
1156    });
1157
1158    let session = task.await.unwrap();
1159    let client = session.update(cx, |session, _| session.adapter_client().unwrap());
1160
1161    client
1162        .on_request::<dap::requests::Threads, _>(move |_, _| {
1163            Ok(dap::ThreadsResponse {
1164                threads: vec![dap::Thread {
1165                    id: 1,
1166                    name: "Thread 1".into(),
1167                }],
1168            })
1169        })
1170        .await;
1171
1172    client
1173        .on_request::<Initialize, _>(move |_, _| {
1174            Ok(dap::Capabilities {
1175                supports_step_back: Some(false),
1176                ..Default::default()
1177            })
1178        })
1179        .await;
1180
1181    client.on_request::<Launch, _>(move |_, _| Ok(())).await;
1182
1183    let stack_frames = vec![
1184        StackFrame {
1185            id: 1,
1186            name: "Stack Frame 1".into(),
1187            source: Some(dap::Source {
1188                name: Some("test.js".into()),
1189                path: Some(path!("/project/src/test.js").into()),
1190                source_reference: None,
1191                presentation_hint: None,
1192                origin: None,
1193                sources: None,
1194                adapter_data: None,
1195                checksums: None,
1196            }),
1197            line: 3,
1198            column: 1,
1199            end_line: None,
1200            end_column: None,
1201            can_restart: None,
1202            instruction_pointer_reference: None,
1203            module_id: None,
1204            presentation_hint: None,
1205        },
1206        StackFrame {
1207            id: 2,
1208            name: "Stack Frame 2".into(),
1209            source: Some(dap::Source {
1210                name: Some("module.js".into()),
1211                path: Some(path!("/project/src/module.js").into()),
1212                source_reference: None,
1213                presentation_hint: None,
1214                origin: None,
1215                sources: None,
1216                adapter_data: None,
1217                checksums: None,
1218            }),
1219            line: 1,
1220            column: 1,
1221            end_line: None,
1222            end_column: None,
1223            can_restart: None,
1224            instruction_pointer_reference: None,
1225            module_id: None,
1226            presentation_hint: None,
1227        },
1228    ];
1229
1230    client
1231        .on_request::<StackTrace, _>({
1232            let stack_frames = Arc::new(stack_frames.clone());
1233            move |_, args| {
1234                assert_eq!(1, args.thread_id);
1235
1236                Ok(dap::StackTraceResponse {
1237                    stack_frames: (*stack_frames).clone(),
1238                    total_frames: None,
1239                })
1240            }
1241        })
1242        .await;
1243
1244    let frame_1_scopes = vec![Scope {
1245        name: "Frame 1 Scope 1".into(),
1246        presentation_hint: None,
1247        variables_reference: 2,
1248        named_variables: None,
1249        indexed_variables: None,
1250        expensive: false,
1251        source: None,
1252        line: None,
1253        column: None,
1254        end_line: None,
1255        end_column: None,
1256    }];
1257
1258    let made_scopes_request = Arc::new(AtomicBool::new(false));
1259
1260    client
1261        .on_request::<Scopes, _>({
1262            let frame_1_scopes = Arc::new(frame_1_scopes.clone());
1263            let made_scopes_request = made_scopes_request.clone();
1264            move |_, args| {
1265                assert_eq!(1, args.frame_id);
1266                assert!(
1267                    !made_scopes_request.load(Ordering::SeqCst),
1268                    "We should be caching the scope request"
1269                );
1270
1271                made_scopes_request.store(true, Ordering::SeqCst);
1272
1273                Ok(dap::ScopesResponse {
1274                    scopes: (*frame_1_scopes).clone(),
1275                })
1276            }
1277        })
1278        .await;
1279
1280    let frame_1_variables = vec![
1281        Variable {
1282            name: "variable1".into(),
1283            value: "value 1".into(),
1284            type_: None,
1285            presentation_hint: None,
1286            evaluate_name: None,
1287            variables_reference: 0,
1288            named_variables: None,
1289            indexed_variables: None,
1290            memory_reference: None,
1291            declaration_location_reference: None,
1292            value_location_reference: None,
1293        },
1294        Variable {
1295            name: "variable2".into(),
1296            value: "value 2".into(),
1297            type_: None,
1298            presentation_hint: None,
1299            evaluate_name: None,
1300            variables_reference: 0,
1301            named_variables: None,
1302            indexed_variables: None,
1303            memory_reference: None,
1304            declaration_location_reference: None,
1305            value_location_reference: None,
1306        },
1307    ];
1308
1309    client
1310        .on_request::<Variables, _>({
1311            let frame_1_variables = Arc::new(frame_1_variables.clone());
1312            move |_, args| {
1313                assert_eq!(2, args.variables_reference);
1314
1315                Ok(dap::VariablesResponse {
1316                    variables: (*frame_1_variables).clone(),
1317                })
1318            }
1319        })
1320        .await;
1321
1322    let running_state = active_debug_session_panel(workspace, cx).update_in(cx, |item, _, cx| {
1323        let state = item
1324            .mode()
1325            .as_running()
1326            .expect("Session should be running by this point")
1327            .clone();
1328
1329        state.update(cx, |state, cx| {
1330            state.set_thread_item(crate::session::ThreadItem::Modules, cx)
1331        });
1332        state
1333    });
1334
1335    client
1336        .fake_event(dap::messages::Events::Stopped(dap::StoppedEvent {
1337            reason: dap::StoppedEventReason::Pause,
1338            description: None,
1339            thread_id: Some(1),
1340            preserve_focus_hint: None,
1341            text: None,
1342            all_threads_stopped: None,
1343            hit_breakpoint_ids: None,
1344        }))
1345        .await;
1346
1347    cx.run_until_parked();
1348
1349    // We shouldn't make any variable requests unless we're rendering the variable list
1350    running_state.update_in(cx, |running_state, window, cx| {
1351        let variable_list = running_state.variable_list().read(cx);
1352        let empty: Vec<dap::Variable> = vec![];
1353
1354        assert_eq!(empty, variable_list.variables());
1355        assert!(!made_scopes_request.load(Ordering::SeqCst));
1356
1357        cx.focus_self(window);
1358        running_state.set_thread_item(crate::session::ThreadItem::Variables, cx);
1359    });
1360
1361    cx.run_until_parked();
1362
1363    running_state.update(cx, |running_state, cx| {
1364        let (stack_frame_list, stack_frame_id) =
1365            running_state.stack_frame_list().update(cx, |list, _| {
1366                (list.flatten_entries(), list.current_stack_frame_id())
1367            });
1368
1369        assert_eq!(Some(1), stack_frame_id);
1370        assert_eq!(stack_frames, stack_frame_list);
1371
1372        let variable_list = running_state.variable_list().read(cx);
1373
1374        assert_eq!(frame_1_variables, variable_list.variables());
1375        assert!(made_scopes_request.load(Ordering::SeqCst));
1376    });
1377
1378    let shutdown_session = project.update(cx, |project, cx| {
1379        project.dap_store().update(cx, |dap_store, cx| {
1380            dap_store.shutdown_session(session.read(cx).session_id(), cx)
1381        })
1382    });
1383
1384    shutdown_session.await.unwrap();
1385}
1386
1387#[gpui::test]
1388async fn test_it_fetches_scopes_variables_when_you_select_a_stack_frame(
1389    executor: BackgroundExecutor,
1390    cx: &mut TestAppContext,
1391) {
1392    init_test(cx);
1393
1394    let fs = FakeFs::new(executor.clone());
1395
1396    let test_file_content = r#"
1397        import { SOME_VALUE } './module.js';
1398
1399        console.log(SOME_VALUE);
1400    "#
1401    .unindent();
1402
1403    let module_file_content = r#"
1404        export SOME_VALUE = 'some value';
1405    "#
1406    .unindent();
1407
1408    fs.insert_tree(
1409        path!("/project"),
1410        json!({
1411           "src": {
1412               "test.js": test_file_content,
1413               "module.js": module_file_content,
1414           }
1415        }),
1416    )
1417    .await;
1418
1419    let project = Project::test(fs, [path!("/project").as_ref()], cx).await;
1420    let workspace = init_test_workspace(&project, cx).await;
1421    workspace
1422        .update(cx, |workspace, window, cx| {
1423            workspace.focus_panel::<DebugPanel>(window, cx);
1424        })
1425        .unwrap();
1426    let cx = &mut VisualTestContext::from_window(*workspace, cx);
1427
1428    let task = project.update(cx, |project, cx| {
1429        project.start_debug_session(
1430            dap::test_config(dap::DebugRequestType::Launch, None, None),
1431            cx,
1432        )
1433    });
1434
1435    let session = task.await.unwrap();
1436    let client = session.update(cx, |session, _| session.adapter_client().unwrap());
1437
1438    client
1439        .on_request::<dap::requests::Threads, _>(move |_, _| {
1440            Ok(dap::ThreadsResponse {
1441                threads: vec![dap::Thread {
1442                    id: 1,
1443                    name: "Thread 1".into(),
1444                }],
1445            })
1446        })
1447        .await;
1448
1449    client
1450        .on_request::<Initialize, _>(move |_, _| {
1451            Ok(dap::Capabilities {
1452                supports_step_back: Some(false),
1453                ..Default::default()
1454            })
1455        })
1456        .await;
1457
1458    client.on_request::<Launch, _>(move |_, _| Ok(())).await;
1459
1460    let stack_frames = vec![
1461        StackFrame {
1462            id: 1,
1463            name: "Stack Frame 1".into(),
1464            source: Some(dap::Source {
1465                name: Some("test.js".into()),
1466                path: Some(path!("/project/src/test.js").into()),
1467                source_reference: None,
1468                presentation_hint: None,
1469                origin: None,
1470                sources: None,
1471                adapter_data: None,
1472                checksums: None,
1473            }),
1474            line: 3,
1475            column: 1,
1476            end_line: None,
1477            end_column: None,
1478            can_restart: None,
1479            instruction_pointer_reference: None,
1480            module_id: None,
1481            presentation_hint: None,
1482        },
1483        StackFrame {
1484            id: 2,
1485            name: "Stack Frame 2".into(),
1486            source: Some(dap::Source {
1487                name: Some("module.js".into()),
1488                path: Some(path!("/project/src/module.js").into()),
1489                source_reference: None,
1490                presentation_hint: None,
1491                origin: None,
1492                sources: None,
1493                adapter_data: None,
1494                checksums: None,
1495            }),
1496            line: 1,
1497            column: 1,
1498            end_line: None,
1499            end_column: None,
1500            can_restart: None,
1501            instruction_pointer_reference: None,
1502            module_id: None,
1503            presentation_hint: None,
1504        },
1505    ];
1506
1507    client
1508        .on_request::<StackTrace, _>({
1509            let stack_frames = Arc::new(stack_frames.clone());
1510            move |_, args| {
1511                assert_eq!(1, args.thread_id);
1512
1513                Ok(dap::StackTraceResponse {
1514                    stack_frames: (*stack_frames).clone(),
1515                    total_frames: None,
1516                })
1517            }
1518        })
1519        .await;
1520
1521    let frame_1_scopes = vec![Scope {
1522        name: "Frame 1 Scope 1".into(),
1523        presentation_hint: None,
1524        variables_reference: 2,
1525        named_variables: None,
1526        indexed_variables: None,
1527        expensive: false,
1528        source: None,
1529        line: None,
1530        column: None,
1531        end_line: None,
1532        end_column: None,
1533    }];
1534
1535    // add handlers for fetching the second stack frame's scopes and variables
1536    // after the user clicked the stack frame
1537    let frame_2_scopes = vec![Scope {
1538        name: "Frame 2 Scope 1".into(),
1539        presentation_hint: None,
1540        variables_reference: 3,
1541        named_variables: None,
1542        indexed_variables: None,
1543        expensive: false,
1544        source: None,
1545        line: None,
1546        column: None,
1547        end_line: None,
1548        end_column: None,
1549    }];
1550
1551    let called_second_stack_frame = Arc::new(AtomicBool::new(false));
1552    let called_first_stack_frame = Arc::new(AtomicBool::new(false));
1553
1554    client
1555        .on_request::<Scopes, _>({
1556            let frame_1_scopes = Arc::new(frame_1_scopes.clone());
1557            let frame_2_scopes = Arc::new(frame_2_scopes.clone());
1558            let called_first_stack_frame = called_first_stack_frame.clone();
1559            let called_second_stack_frame = called_second_stack_frame.clone();
1560            move |_, args| match args.frame_id {
1561                1 => {
1562                    called_first_stack_frame.store(true, Ordering::SeqCst);
1563                    Ok(dap::ScopesResponse {
1564                        scopes: (*frame_1_scopes).clone(),
1565                    })
1566                }
1567                2 => {
1568                    called_second_stack_frame.store(true, Ordering::SeqCst);
1569
1570                    Ok(dap::ScopesResponse {
1571                        scopes: (*frame_2_scopes).clone(),
1572                    })
1573                }
1574                _ => panic!("Made a scopes request with an invalid frame id"),
1575            }
1576        })
1577        .await;
1578
1579    let frame_1_variables = vec![
1580        Variable {
1581            name: "variable1".into(),
1582            value: "value 1".into(),
1583            type_: None,
1584            presentation_hint: None,
1585            evaluate_name: None,
1586            variables_reference: 0,
1587            named_variables: None,
1588            indexed_variables: None,
1589            memory_reference: None,
1590            declaration_location_reference: None,
1591            value_location_reference: None,
1592        },
1593        Variable {
1594            name: "variable2".into(),
1595            value: "value 2".into(),
1596            type_: None,
1597            presentation_hint: None,
1598            evaluate_name: None,
1599            variables_reference: 0,
1600            named_variables: None,
1601            indexed_variables: None,
1602            memory_reference: None,
1603            declaration_location_reference: None,
1604            value_location_reference: None,
1605        },
1606    ];
1607
1608    let frame_2_variables = vec![
1609        Variable {
1610            name: "variable3".into(),
1611            value: "old value 1".into(),
1612            type_: None,
1613            presentation_hint: None,
1614            evaluate_name: None,
1615            variables_reference: 0,
1616            named_variables: None,
1617            indexed_variables: None,
1618            memory_reference: None,
1619            declaration_location_reference: None,
1620            value_location_reference: None,
1621        },
1622        Variable {
1623            name: "variable4".into(),
1624            value: "old value 2".into(),
1625            type_: None,
1626            presentation_hint: None,
1627            evaluate_name: None,
1628            variables_reference: 0,
1629            named_variables: None,
1630            indexed_variables: None,
1631            memory_reference: None,
1632            declaration_location_reference: None,
1633            value_location_reference: None,
1634        },
1635    ];
1636
1637    client
1638        .on_request::<Variables, _>({
1639            let frame_1_variables = Arc::new(frame_1_variables.clone());
1640            move |_, args| {
1641                assert_eq!(2, args.variables_reference);
1642
1643                Ok(dap::VariablesResponse {
1644                    variables: (*frame_1_variables).clone(),
1645                })
1646            }
1647        })
1648        .await;
1649
1650    client
1651        .fake_event(dap::messages::Events::Stopped(dap::StoppedEvent {
1652            reason: dap::StoppedEventReason::Pause,
1653            description: None,
1654            thread_id: Some(1),
1655            preserve_focus_hint: None,
1656            text: None,
1657            all_threads_stopped: None,
1658            hit_breakpoint_ids: None,
1659        }))
1660        .await;
1661
1662    cx.run_until_parked();
1663
1664    let running_state =
1665        active_debug_session_panel(workspace, cx).update_in(cx, |item, window, cx| {
1666            cx.focus_self(window);
1667            item.mode()
1668                .as_running()
1669                .expect("Session should be running by this point")
1670                .clone()
1671        });
1672
1673    cx.run_until_parked();
1674
1675    running_state.update(cx, |running_state, cx| {
1676        let (stack_frame_list, stack_frame_id) =
1677            running_state.stack_frame_list().update(cx, |list, _| {
1678                (list.flatten_entries(), list.current_stack_frame_id())
1679            });
1680
1681        let variable_list = running_state.variable_list().read(cx);
1682        let variables = variable_list.variables();
1683
1684        assert_eq!(Some(1), stack_frame_id);
1685        assert_eq!(
1686            running_state
1687                .stack_frame_list()
1688                .read(cx)
1689                .current_stack_frame_id(),
1690            Some(1)
1691        );
1692
1693        assert!(
1694            called_first_stack_frame.load(std::sync::atomic::Ordering::SeqCst),
1695            "Request scopes shouldn't be called before it's needed"
1696        );
1697        assert!(
1698            !called_second_stack_frame.load(std::sync::atomic::Ordering::SeqCst),
1699            "Request scopes shouldn't be called before it's needed"
1700        );
1701
1702        assert_eq!(stack_frames, stack_frame_list);
1703        assert_eq!(frame_1_variables, variables);
1704    });
1705
1706    client
1707        .on_request::<Variables, _>({
1708            let frame_2_variables = Arc::new(frame_2_variables.clone());
1709            move |_, args| {
1710                assert_eq!(3, args.variables_reference);
1711
1712                Ok(dap::VariablesResponse {
1713                    variables: (*frame_2_variables).clone(),
1714                })
1715            }
1716        })
1717        .await;
1718
1719    running_state
1720        .update_in(cx, |running_state, window, cx| {
1721            running_state
1722                .stack_frame_list()
1723                .update(cx, |stack_frame_list, cx| {
1724                    stack_frame_list.select_stack_frame(&stack_frames[1], true, window, cx)
1725                })
1726        })
1727        .await
1728        .unwrap();
1729
1730    cx.run_until_parked();
1731
1732    running_state.update(cx, |running_state, cx| {
1733        let (stack_frame_list, stack_frame_id) =
1734            running_state.stack_frame_list().update(cx, |list, _| {
1735                (list.flatten_entries(), list.current_stack_frame_id())
1736            });
1737
1738        let variable_list = running_state.variable_list().read(cx);
1739        let variables = variable_list.variables();
1740
1741        assert_eq!(Some(2), stack_frame_id);
1742        assert!(
1743            called_second_stack_frame.load(std::sync::atomic::Ordering::SeqCst),
1744            "Request scopes shouldn't be called before it's needed"
1745        );
1746
1747        assert_eq!(stack_frames, stack_frame_list);
1748
1749        assert_eq!(variables, frame_2_variables,);
1750    });
1751
1752    let shutdown_session = project.update(cx, |project, cx| {
1753        project.dap_store().update(cx, |dap_store, cx| {
1754            dap_store.shutdown_session(session.read(cx).session_id(), cx)
1755        })
1756    });
1757
1758    shutdown_session.await.unwrap();
1759}