variable_list.rs

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