session.rs

   1use crate::project_settings::ProjectSettings;
   2
   3use super::breakpoint_store::{BreakpointStore, BreakpointStoreEvent, BreakpointUpdatedReason};
   4use super::dap_command::{
   5    self, Attach, ConfigurationDone, ContinueCommand, DapCommand, DisconnectCommand,
   6    EvaluateCommand, Initialize, Launch, LoadedSourcesCommand, LocalDapCommand, LocationsCommand,
   7    ModulesCommand, NextCommand, PauseCommand, RestartCommand, RestartStackFrameCommand,
   8    ScopesCommand, SetVariableValueCommand, StackTraceCommand, StepBackCommand, StepCommand,
   9    StepInCommand, StepOutCommand, TerminateCommand, TerminateThreadsCommand, ThreadsCommand,
  10    VariablesCommand,
  11};
  12use super::dap_store::DapAdapterDelegate;
  13use anyhow::{anyhow, Result};
  14use collections::{HashMap, HashSet, IndexMap, IndexSet};
  15use dap::adapters::{DebugAdapter, DebugAdapterBinary};
  16use dap::messages::Response;
  17use dap::OutputEventCategory;
  18use dap::{
  19    adapters::{DapDelegate, DapStatus},
  20    client::{DebugAdapterClient, SessionId},
  21    messages::{Events, Message},
  22    Capabilities, ContinueArguments, EvaluateArgumentsContext, Module, Source, StackFrameId,
  23    SteppingGranularity, StoppedEvent, VariableReference,
  24};
  25use dap_adapters::build_adapter;
  26use futures::channel::oneshot;
  27use futures::{future::Shared, FutureExt};
  28use gpui::{
  29    App, AppContext, AsyncApp, BackgroundExecutor, Context, Entity, EventEmitter, Task, WeakEntity,
  30};
  31use rpc::AnyProtoClient;
  32use serde_json::{json, Value};
  33use settings::Settings;
  34use smol::stream::StreamExt;
  35use std::any::TypeId;
  36use std::path::PathBuf;
  37use std::u64;
  38use std::{
  39    any::Any,
  40    collections::hash_map::Entry,
  41    hash::{Hash, Hasher},
  42    path::Path,
  43    sync::Arc,
  44};
  45use task::DebugAdapterConfig;
  46use text::{PointUtf16, ToPointUtf16};
  47use util::{merge_json_value_into, ResultExt};
  48
  49#[derive(Debug, Copy, Clone, Hash, PartialEq, PartialOrd, Ord, Eq)]
  50#[repr(transparent)]
  51pub struct ThreadId(pub u64);
  52
  53impl ThreadId {
  54    pub const MIN: ThreadId = ThreadId(u64::MIN);
  55    pub const MAX: ThreadId = ThreadId(u64::MAX);
  56}
  57
  58impl From<u64> for ThreadId {
  59    fn from(id: u64) -> Self {
  60        Self(id)
  61    }
  62}
  63
  64#[derive(Clone, Debug)]
  65pub struct StackFrame {
  66    pub dap: dap::StackFrame,
  67    pub scopes: Vec<dap::Scope>,
  68}
  69
  70impl From<dap::StackFrame> for StackFrame {
  71    fn from(stack_frame: dap::StackFrame) -> Self {
  72        Self {
  73            scopes: vec![],
  74            dap: stack_frame,
  75        }
  76    }
  77}
  78
  79#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
  80pub enum ThreadStatus {
  81    #[default]
  82    Running,
  83    Stopped,
  84    Stepping,
  85    Exited,
  86    Ended,
  87}
  88
  89impl ThreadStatus {
  90    pub fn label(&self) -> &'static str {
  91        match self {
  92            ThreadStatus::Running => "Running",
  93            ThreadStatus::Stopped => "Stopped",
  94            ThreadStatus::Stepping => "Stepping",
  95            ThreadStatus::Exited => "Exited",
  96            ThreadStatus::Ended => "Ended",
  97        }
  98    }
  99}
 100
 101#[derive(Debug)]
 102pub struct Thread {
 103    dap: dap::Thread,
 104    stack_frame_ids: IndexSet<StackFrameId>,
 105    _has_stopped: bool,
 106}
 107
 108impl From<dap::Thread> for Thread {
 109    fn from(dap: dap::Thread) -> Self {
 110        Self {
 111            dap,
 112            stack_frame_ids: Default::default(),
 113            _has_stopped: false,
 114        }
 115    }
 116}
 117
 118type UpstreamProjectId = u64;
 119
 120struct RemoteConnection {
 121    _client: AnyProtoClient,
 122    _upstream_project_id: UpstreamProjectId,
 123}
 124
 125impl RemoteConnection {
 126    fn send_proto_client_request<R: DapCommand>(
 127        &self,
 128        _request: R,
 129        _session_id: SessionId,
 130        cx: &mut App,
 131    ) -> Task<Result<R::Response>> {
 132        // let message = request.to_proto(session_id, self.upstream_project_id);
 133        // let upstream_client = self.client.clone();
 134        cx.background_executor().spawn(async move {
 135            // debugger(todo): Properly send messages when we wrap dap_commands in envelopes again
 136            // let response = upstream_client.request(message).await?;
 137            // request.response_from_proto(response)
 138            Err(anyhow!("Sending dap commands over RPC isn't supported yet"))
 139        })
 140    }
 141
 142    fn request<R: DapCommand>(
 143        &self,
 144        request: R,
 145        session_id: SessionId,
 146        cx: &mut App,
 147    ) -> Task<Result<R::Response>>
 148    where
 149        <R::DapRequest as dap::requests::Request>::Response: 'static,
 150        <R::DapRequest as dap::requests::Request>::Arguments: 'static + Send,
 151    {
 152        return self.send_proto_client_request::<R>(request, session_id, cx);
 153    }
 154}
 155
 156enum Mode {
 157    Local(LocalMode),
 158    Remote(RemoteConnection),
 159}
 160
 161#[derive(Clone)]
 162pub struct LocalMode {
 163    client: Arc<DebugAdapterClient>,
 164    config: DebugAdapterConfig,
 165    adapter: Arc<dyn DebugAdapter>,
 166    breakpoint_store: Entity<BreakpointStore>,
 167}
 168
 169fn client_source(abs_path: &Path) -> dap::Source {
 170    dap::Source {
 171        name: abs_path
 172            .file_name()
 173            .map(|filename| filename.to_string_lossy().to_string()),
 174        path: Some(abs_path.to_string_lossy().to_string()),
 175        source_reference: None,
 176        presentation_hint: None,
 177        origin: None,
 178        sources: None,
 179        adapter_data: None,
 180        checksums: None,
 181    }
 182}
 183
 184impl LocalMode {
 185    fn new(
 186        session_id: SessionId,
 187        parent_session: Option<Entity<Session>>,
 188        breakpoint_store: Entity<BreakpointStore>,
 189        config: DebugAdapterConfig,
 190        delegate: DapAdapterDelegate,
 191        messages_tx: futures::channel::mpsc::UnboundedSender<Message>,
 192        cx: AsyncApp,
 193    ) -> Task<Result<(Self, Capabilities)>> {
 194        cx.spawn(async move |cx| {
 195            let (adapter, binary) = Self::get_adapter_binary(&config, &delegate,  cx).await?;
 196
 197            let message_handler = Box::new(move |message| {
 198                messages_tx.unbounded_send(message).ok();
 199            });
 200
 201            let client = Arc::new(
 202                if let Some(client) = parent_session
 203                    .and_then(|session| cx.update(|cx| session.read(cx).adapter_client()).ok())
 204                    .flatten()
 205                {
 206                    client
 207                        .reconnect(session_id, binary, message_handler, cx.clone())
 208                        .await?
 209                } else {
 210                    DebugAdapterClient::start(
 211                        session_id,
 212                        adapter.name(),
 213                        binary,
 214                        message_handler,
 215                        cx.clone(),
 216                    )
 217                    .await?
 218                },
 219            );
 220
 221            let adapter_id = adapter.name().to_string().to_owned();
 222            let session = Self {
 223                client,
 224                adapter,
 225                breakpoint_store,
 226                config: config.clone(),
 227            };
 228
 229            #[cfg(any(test, feature = "test-support"))]
 230            {
 231                let dap::DebugAdapterKind::Fake((fail, caps)) = session.config.kind.clone() else {
 232                    panic!("Only fake debug adapter configs should be used in tests");
 233                };
 234
 235                session
 236                    .client
 237                    .on_request::<dap::requests::Initialize, _>(move |_, _| Ok(caps.clone()))
 238                    .await;
 239
 240                let paths = cx.update(|cx| session.breakpoint_store.read(cx).breakpoint_paths()).expect("Breakpoint store should exist in all tests that start debuggers");
 241
 242                session.client.on_request::<dap::requests::SetBreakpoints, _>(move |_, args| {
 243                    let p = Arc::from(Path::new(&args.source.path.unwrap()));
 244                    if !paths.contains(&p) {
 245                        panic!("Sent breakpoints for path without any")
 246                    }
 247
 248                    Ok(dap::SetBreakpointsResponse {
 249                        breakpoints: Vec::default(),
 250                    })
 251                }).await;
 252
 253                match config.request.clone() {
 254                    dap::DebugRequestType::Launch if fail => {
 255                        session
 256                            .client
 257                            .on_request::<dap::requests::Launch, _>(move |_, _| {
 258                                Err(dap::ErrorResponse {
 259                                    error: Some(dap::Message {
 260                                        id: 1,
 261                                        format: "error".into(),
 262                                        variables: None,
 263                                        send_telemetry: None,
 264                                        show_user: None,
 265                                        url: None,
 266                                        url_label: None,
 267                                    }),
 268                                })
 269                            })
 270                            .await;
 271                    }
 272                    dap::DebugRequestType::Launch => {
 273                        session
 274                            .client
 275                            .on_request::<dap::requests::Launch, _>(move |_, _| Ok(()))
 276                            .await;
 277                    }
 278                    dap::DebugRequestType::Attach(_) if fail => {
 279                        session
 280                            .client
 281                            .on_request::<dap::requests::Attach, _>(move |_, _| {
 282                                Err(dap::ErrorResponse {
 283                                    error: Some(dap::Message {
 284                                        id: 1,
 285                                        format: "error".into(),
 286                                        variables: None,
 287                                        send_telemetry: None,
 288                                        show_user: None,
 289                                        url: None,
 290                                        url_label: None,
 291                                    }),
 292                                })
 293                            })
 294                            .await;
 295                    }
 296                    dap::DebugRequestType::Attach(attach_config) => {
 297                        session
 298                            .client
 299                            .on_request::<dap::requests::Attach, _>(move |_, args| {
 300                                assert_eq!(
 301                                    json!({"request": "attach", "process_id": attach_config.process_id.unwrap()}),
 302                                    args.raw
 303                                );
 304
 305                                Ok(())
 306                            })
 307                            .await;
 308                    }
 309                }
 310
 311                session.client.on_request::<dap::requests::Disconnect, _>(move |_, _| Ok(())).await;
 312                session.client.fake_event(Events::Initialized(None)).await;
 313            }
 314
 315            let capabilities = session
 316                .request(Initialize { adapter_id }, cx.background_executor().clone())
 317                .await?;
 318
 319            Ok((session, capabilities))
 320        })
 321    }
 322
 323    fn unset_breakpoints_from_paths(&self, paths: &Vec<Arc<Path>>, cx: &mut App) -> Task<()> {
 324        let tasks: Vec<_> = paths
 325            .into_iter()
 326            .map(|path| {
 327                self.request(
 328                    dap_command::SetBreakpoints {
 329                        source: client_source(path),
 330                        source_modified: None,
 331                        breakpoints: vec![],
 332                    },
 333                    cx.background_executor().clone(),
 334                )
 335            })
 336            .collect();
 337
 338        cx.background_spawn(async move {
 339            futures::future::join_all(tasks)
 340                .await
 341                .iter()
 342                .for_each(|res| match res {
 343                    Ok(_) => {}
 344                    Err(err) => {
 345                        log::warn!("Set breakpoints request failed: {}", err);
 346                    }
 347                });
 348        })
 349    }
 350
 351    fn send_breakpoints_from_path(
 352        &self,
 353        abs_path: Arc<Path>,
 354        reason: BreakpointUpdatedReason,
 355        cx: &mut App,
 356    ) -> Task<()> {
 357        let breakpoints = self
 358            .breakpoint_store
 359            .read_with(cx, |store, cx| store.breakpoints_from_path(&abs_path, cx))
 360            .into_iter()
 361            .filter(|bp| bp.state.is_enabled())
 362            .map(Into::into)
 363            .collect();
 364
 365        let task = self.request(
 366            dap_command::SetBreakpoints {
 367                source: client_source(&abs_path),
 368                source_modified: Some(matches!(reason, BreakpointUpdatedReason::FileSaved)),
 369                breakpoints,
 370            },
 371            cx.background_executor().clone(),
 372        );
 373
 374        cx.background_spawn(async move {
 375            match task.await {
 376                Ok(_) => {}
 377                Err(err) => log::warn!("Set breakpoints request failed for path: {}", err),
 378            }
 379        })
 380    }
 381
 382    fn send_all_breakpoints(&self, ignore_breakpoints: bool, cx: &App) -> Task<()> {
 383        let mut breakpoint_tasks = Vec::new();
 384        let breakpoints = self
 385            .breakpoint_store
 386            .read_with(cx, |store, cx| store.all_breakpoints(cx));
 387
 388        for (path, breakpoints) in breakpoints {
 389            let breakpoints = if ignore_breakpoints {
 390                vec![]
 391            } else {
 392                breakpoints
 393                    .into_iter()
 394                    .filter(|bp| bp.state.is_enabled())
 395                    .map(Into::into)
 396                    .collect()
 397            };
 398
 399            breakpoint_tasks.push(self.request(
 400                dap_command::SetBreakpoints {
 401                    source: client_source(&path),
 402                    source_modified: Some(false),
 403                    breakpoints,
 404                },
 405                cx.background_executor().clone(),
 406            ));
 407        }
 408
 409        cx.background_spawn(async move {
 410            futures::future::join_all(breakpoint_tasks)
 411                .await
 412                .iter()
 413                .for_each(|res| match res {
 414                    Ok(_) => {}
 415                    Err(err) => {
 416                        log::warn!("Set breakpoints request failed: {}", err);
 417                    }
 418                });
 419        })
 420    }
 421
 422    async fn get_adapter_binary(
 423        config: &DebugAdapterConfig,
 424        delegate: &DapAdapterDelegate,
 425        cx: &mut AsyncApp,
 426    ) -> Result<(Arc<dyn DebugAdapter>, DebugAdapterBinary)> {
 427        let adapter = build_adapter(&config.kind).await?;
 428
 429        let binary = cx.update(|cx| {
 430            ProjectSettings::get_global(cx)
 431                .dap
 432                .get(&adapter.name())
 433                .and_then(|s| s.binary.as_ref().map(PathBuf::from))
 434        })?;
 435
 436        let binary = match adapter.get_binary(delegate, &config, binary, cx).await {
 437            Err(error) => {
 438                delegate.update_status(
 439                    adapter.name(),
 440                    DapStatus::Failed {
 441                        error: error.to_string(),
 442                    },
 443                );
 444
 445                return Err(error);
 446            }
 447            Ok(mut binary) => {
 448                delegate.update_status(adapter.name(), DapStatus::None);
 449
 450                let shell_env = delegate.shell_env().await;
 451                let mut envs = binary.envs.unwrap_or_default();
 452                envs.extend(shell_env);
 453                binary.envs = Some(envs);
 454
 455                binary
 456            }
 457        };
 458
 459        Ok((adapter, binary))
 460    }
 461
 462    pub fn initialize_sequence(
 463        &self,
 464        capabilities: &Capabilities,
 465        initialized_rx: oneshot::Receiver<()>,
 466        cx: &App,
 467    ) -> Task<Result<()>> {
 468        let mut raw = self.adapter.request_args(&self.config);
 469        merge_json_value_into(
 470            self.config.initialize_args.clone().unwrap_or(json!({})),
 471            &mut raw,
 472        );
 473
 474        // Of relevance: https://github.com/microsoft/vscode/issues/4902#issuecomment-368583522
 475        let launch = match &self.config.request {
 476            dap::DebugRequestType::Launch => {
 477                self.request(Launch { raw }, cx.background_executor().clone())
 478            }
 479            dap::DebugRequestType::Attach(_) => {
 480                self.request(Attach { raw }, cx.background_executor().clone())
 481            }
 482        };
 483
 484        let configuration_done_supported = ConfigurationDone::is_supported(capabilities);
 485
 486        let configuration_sequence = cx.spawn({
 487            let this = self.clone();
 488            async move |cx| {
 489                initialized_rx.await?;
 490                // todo(debugger) figure out if we want to handle a breakpoint response error
 491                // This will probably consist of letting a user know that breakpoints failed to be set
 492                cx.update(|cx| this.send_all_breakpoints(false, cx))?.await;
 493
 494                if configuration_done_supported {
 495                    this.request(ConfigurationDone, cx.background_executor().clone())
 496                } else {
 497                    Task::ready(Ok(()))
 498                }
 499                .await
 500            }
 501        });
 502
 503        cx.background_spawn(async move {
 504            futures::future::try_join(launch, configuration_sequence).await?;
 505            Ok(())
 506        })
 507    }
 508
 509    fn request<R: LocalDapCommand>(
 510        &self,
 511        request: R,
 512        executor: BackgroundExecutor,
 513    ) -> Task<Result<R::Response>>
 514    where
 515        <R::DapRequest as dap::requests::Request>::Response: 'static,
 516        <R::DapRequest as dap::requests::Request>::Arguments: 'static + Send,
 517    {
 518        let request = Arc::new(request);
 519
 520        let request_clone = request.clone();
 521        let connection = self.client.clone();
 522        let request_task = executor.spawn(async move {
 523            let args = request_clone.to_dap();
 524            connection.request::<R::DapRequest>(args).await
 525        });
 526
 527        executor.spawn(async move {
 528            let response = request.response_from_dap(request_task.await?);
 529            response
 530        })
 531    }
 532}
 533impl From<RemoteConnection> for Mode {
 534    fn from(value: RemoteConnection) -> Self {
 535        Self::Remote(value)
 536    }
 537}
 538
 539impl Mode {
 540    fn request_dap<R: DapCommand>(
 541        &self,
 542        session_id: SessionId,
 543        request: R,
 544        cx: &mut Context<Session>,
 545    ) -> Task<Result<R::Response>>
 546    where
 547        <R::DapRequest as dap::requests::Request>::Response: 'static,
 548        <R::DapRequest as dap::requests::Request>::Arguments: 'static + Send,
 549    {
 550        match self {
 551            Mode::Local(debug_adapter_client) => {
 552                debug_adapter_client.request(request, cx.background_executor().clone())
 553            }
 554            Mode::Remote(remote_connection) => remote_connection.request(request, session_id, cx),
 555        }
 556    }
 557}
 558
 559#[derive(Default)]
 560struct ThreadStates {
 561    global_state: Option<ThreadStatus>,
 562    known_thread_states: IndexMap<ThreadId, ThreadStatus>,
 563}
 564
 565impl ThreadStates {
 566    fn stop_all_threads(&mut self) {
 567        self.global_state = Some(ThreadStatus::Stopped);
 568        self.known_thread_states.clear();
 569    }
 570
 571    fn exit_all_threads(&mut self) {
 572        self.global_state = Some(ThreadStatus::Exited);
 573        self.known_thread_states.clear();
 574    }
 575
 576    fn continue_all_threads(&mut self) {
 577        self.global_state = Some(ThreadStatus::Running);
 578        self.known_thread_states.clear();
 579    }
 580
 581    fn stop_thread(&mut self, thread_id: ThreadId) {
 582        self.known_thread_states
 583            .insert(thread_id, ThreadStatus::Stopped);
 584    }
 585
 586    fn continue_thread(&mut self, thread_id: ThreadId) {
 587        self.known_thread_states
 588            .insert(thread_id, ThreadStatus::Running);
 589    }
 590
 591    fn process_step(&mut self, thread_id: ThreadId) {
 592        self.known_thread_states
 593            .insert(thread_id, ThreadStatus::Stepping);
 594    }
 595
 596    fn thread_status(&self, thread_id: ThreadId) -> ThreadStatus {
 597        self.thread_state(thread_id)
 598            .unwrap_or(ThreadStatus::Running)
 599    }
 600
 601    fn thread_state(&self, thread_id: ThreadId) -> Option<ThreadStatus> {
 602        self.known_thread_states
 603            .get(&thread_id)
 604            .copied()
 605            .or(self.global_state)
 606    }
 607
 608    fn exit_thread(&mut self, thread_id: ThreadId) {
 609        self.known_thread_states
 610            .insert(thread_id, ThreadStatus::Exited);
 611    }
 612
 613    fn any_stopped_thread(&self) -> bool {
 614        self.global_state
 615            .is_some_and(|state| state == ThreadStatus::Stopped)
 616            || self
 617                .known_thread_states
 618                .values()
 619                .any(|status| *status == ThreadStatus::Stopped)
 620    }
 621}
 622const MAX_TRACKED_OUTPUT_EVENTS: usize = 5000;
 623
 624#[derive(Copy, Clone, Default, Debug, PartialEq, PartialOrd, Eq, Ord)]
 625pub struct OutputToken(pub usize);
 626/// Represents a current state of a single debug adapter and provides ways to mutate it.
 627pub struct Session {
 628    mode: Mode,
 629    pub(super) capabilities: Capabilities,
 630    id: SessionId,
 631    child_session_ids: HashSet<SessionId>,
 632    parent_id: Option<SessionId>,
 633    ignore_breakpoints: bool,
 634    modules: Vec<dap::Module>,
 635    loaded_sources: Vec<dap::Source>,
 636    output_token: OutputToken,
 637    output: Box<circular_buffer::CircularBuffer<MAX_TRACKED_OUTPUT_EVENTS, dap::OutputEvent>>,
 638    threads: IndexMap<ThreadId, Thread>,
 639    thread_states: ThreadStates,
 640    variables: HashMap<VariableReference, Vec<dap::Variable>>,
 641    stack_frames: IndexMap<StackFrameId, StackFrame>,
 642    locations: HashMap<u64, dap::LocationsResponse>,
 643    is_session_terminated: bool,
 644    requests: HashMap<TypeId, HashMap<RequestSlot, Shared<Task<Option<()>>>>>,
 645    _background_tasks: Vec<Task<()>>,
 646}
 647
 648trait CacheableCommand: 'static + Send + Sync {
 649    fn as_any(&self) -> &dyn Any;
 650    fn dyn_eq(&self, rhs: &dyn CacheableCommand) -> bool;
 651    fn dyn_hash(&self, hasher: &mut dyn Hasher);
 652    fn as_any_arc(self: Arc<Self>) -> Arc<dyn Any + Send + Sync>;
 653}
 654
 655impl<T> CacheableCommand for T
 656where
 657    T: DapCommand + PartialEq + Eq + Hash,
 658{
 659    fn as_any(&self) -> &dyn Any {
 660        self
 661    }
 662
 663    fn dyn_eq(&self, rhs: &dyn CacheableCommand) -> bool {
 664        rhs.as_any()
 665            .downcast_ref::<Self>()
 666            .map_or(false, |rhs| self == rhs)
 667    }
 668
 669    fn dyn_hash(&self, mut hasher: &mut dyn Hasher) {
 670        T::hash(self, &mut hasher);
 671    }
 672
 673    fn as_any_arc(self: Arc<Self>) -> Arc<dyn Any + Send + Sync> {
 674        self
 675    }
 676}
 677
 678pub(crate) struct RequestSlot(Arc<dyn CacheableCommand>);
 679
 680impl<T: DapCommand + PartialEq + Eq + Hash> From<T> for RequestSlot {
 681    fn from(request: T) -> Self {
 682        Self(Arc::new(request))
 683    }
 684}
 685
 686impl PartialEq for RequestSlot {
 687    fn eq(&self, other: &Self) -> bool {
 688        self.0.dyn_eq(other.0.as_ref())
 689    }
 690}
 691
 692impl Eq for RequestSlot {}
 693
 694impl Hash for RequestSlot {
 695    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
 696        self.0.dyn_hash(state);
 697        self.0.as_any().type_id().hash(state)
 698    }
 699}
 700
 701#[derive(Debug, Clone, Hash, PartialEq, Eq)]
 702pub struct CompletionsQuery {
 703    pub query: String,
 704    pub column: u64,
 705    pub line: Option<u64>,
 706    pub frame_id: Option<u64>,
 707}
 708
 709impl CompletionsQuery {
 710    pub fn new(
 711        buffer: &language::Buffer,
 712        cursor_position: language::Anchor,
 713        frame_id: Option<u64>,
 714    ) -> Self {
 715        let PointUtf16 { row, column } = cursor_position.to_point_utf16(&buffer.snapshot());
 716        Self {
 717            query: buffer.text(),
 718            column: column as u64,
 719            frame_id,
 720            line: Some(row as u64),
 721        }
 722    }
 723}
 724
 725pub enum SessionEvent {
 726    Modules,
 727    LoadedSources,
 728    Stopped(Option<ThreadId>),
 729    StackTrace,
 730    Variables,
 731    Threads,
 732}
 733
 734impl EventEmitter<SessionEvent> for Session {}
 735
 736// local session will send breakpoint updates to DAP for all new breakpoints
 737// remote side will only send breakpoint updates when it is a breakpoint created by that peer
 738// BreakpointStore notifies session on breakpoint changes
 739impl Session {
 740    pub(crate) fn local(
 741        breakpoint_store: Entity<BreakpointStore>,
 742        session_id: SessionId,
 743        parent_session: Option<Entity<Session>>,
 744        delegate: DapAdapterDelegate,
 745        config: DebugAdapterConfig,
 746        start_debugging_requests_tx: futures::channel::mpsc::UnboundedSender<(SessionId, Message)>,
 747        initialized_tx: oneshot::Sender<()>,
 748        cx: &mut App,
 749    ) -> Task<Result<Entity<Self>>> {
 750        let (message_tx, mut message_rx) = futures::channel::mpsc::unbounded();
 751
 752        cx.spawn(async move |cx| {
 753            let (mode, capabilities) = LocalMode::new(
 754                session_id,
 755                parent_session.clone(),
 756                breakpoint_store.clone(),
 757                config.clone(),
 758                delegate,
 759                message_tx,
 760                cx.clone(),
 761            )
 762            .await?;
 763
 764            cx.new(|cx| {
 765                let _background_tasks = vec![cx.spawn(async move |this: WeakEntity<Self>, cx| {
 766                    let mut initialized_tx = Some(initialized_tx);
 767                    while let Some(message) = message_rx.next().await {
 768                        if let Message::Event(event) = message {
 769                            if let Events::Initialized(_) = *event {
 770                                if let Some(tx) = initialized_tx.take() {
 771                                    tx.send(()).ok();
 772                                }
 773                            } else {
 774                                let Ok(_) = this.update(cx, |session, cx| {
 775                                    session.handle_dap_event(event, cx);
 776                                }) else {
 777                                    break;
 778                                };
 779                            }
 780                        } else {
 781                            let Ok(_) =
 782                                start_debugging_requests_tx.unbounded_send((session_id, message))
 783                            else {
 784                                break;
 785                            };
 786                        }
 787                    }
 788                })];
 789
 790                cx.subscribe(&breakpoint_store, |this, _, event, cx| match event {
 791                    BreakpointStoreEvent::BreakpointsUpdated(path, reason) => {
 792                        if let Some(local) = (!this.ignore_breakpoints)
 793                            .then(|| this.as_local_mut())
 794                            .flatten()
 795                        {
 796                            local
 797                                .send_breakpoints_from_path(path.clone(), *reason, cx)
 798                                .detach();
 799                        };
 800                    }
 801                    BreakpointStoreEvent::BreakpointsCleared(paths) => {
 802                        if let Some(local) = (!this.ignore_breakpoints)
 803                            .then(|| this.as_local_mut())
 804                            .flatten()
 805                        {
 806                            local.unset_breakpoints_from_paths(paths, cx).detach();
 807                        }
 808                    }
 809                    BreakpointStoreEvent::ActiveDebugLineChanged => {}
 810                })
 811                .detach();
 812
 813                Self {
 814                    mode: Mode::Local(mode),
 815                    id: session_id,
 816                    child_session_ids: HashSet::default(),
 817                    parent_id: parent_session.map(|session| session.read(cx).id),
 818                    variables: Default::default(),
 819                    capabilities,
 820                    thread_states: ThreadStates::default(),
 821                    output_token: OutputToken(0),
 822                    ignore_breakpoints: false,
 823                    output: circular_buffer::CircularBuffer::boxed(),
 824                    requests: HashMap::default(),
 825                    modules: Vec::default(),
 826                    loaded_sources: Vec::default(),
 827                    threads: IndexMap::default(),
 828                    stack_frames: IndexMap::default(),
 829                    locations: Default::default(),
 830                    _background_tasks,
 831                    is_session_terminated: false,
 832                }
 833            })
 834        })
 835    }
 836
 837    pub(crate) fn remote(
 838        session_id: SessionId,
 839        client: AnyProtoClient,
 840        upstream_project_id: u64,
 841        ignore_breakpoints: bool,
 842    ) -> Self {
 843        Self {
 844            mode: Mode::Remote(RemoteConnection {
 845                _client: client,
 846                _upstream_project_id: upstream_project_id,
 847            }),
 848            id: session_id,
 849            child_session_ids: HashSet::default(),
 850            parent_id: None,
 851            capabilities: Capabilities::default(),
 852            ignore_breakpoints,
 853            variables: Default::default(),
 854            stack_frames: Default::default(),
 855            thread_states: ThreadStates::default(),
 856            output_token: OutputToken(0),
 857            output: circular_buffer::CircularBuffer::boxed(),
 858            requests: HashMap::default(),
 859            modules: Vec::default(),
 860            loaded_sources: Vec::default(),
 861            threads: IndexMap::default(),
 862            _background_tasks: Vec::default(),
 863            locations: Default::default(),
 864            is_session_terminated: false,
 865        }
 866    }
 867
 868    pub fn session_id(&self) -> SessionId {
 869        self.id
 870    }
 871
 872    pub fn child_session_ids(&self) -> HashSet<SessionId> {
 873        self.child_session_ids.clone()
 874    }
 875
 876    pub fn add_child_session_id(&mut self, session_id: SessionId) {
 877        self.child_session_ids.insert(session_id);
 878    }
 879
 880    pub fn remove_child_session_id(&mut self, session_id: SessionId) {
 881        self.child_session_ids.remove(&session_id);
 882    }
 883
 884    pub fn parent_id(&self) -> Option<SessionId> {
 885        self.parent_id
 886    }
 887
 888    pub fn capabilities(&self) -> &Capabilities {
 889        &self.capabilities
 890    }
 891
 892    pub fn configuration(&self) -> Option<DebugAdapterConfig> {
 893        if let Mode::Local(local_mode) = &self.mode {
 894            Some(local_mode.config.clone())
 895        } else {
 896            None
 897        }
 898    }
 899
 900    pub fn is_terminated(&self) -> bool {
 901        self.is_session_terminated
 902    }
 903
 904    pub fn is_local(&self) -> bool {
 905        matches!(self.mode, Mode::Local(_))
 906    }
 907
 908    pub fn as_local_mut(&mut self) -> Option<&mut LocalMode> {
 909        match &mut self.mode {
 910            Mode::Local(local_mode) => Some(local_mode),
 911            Mode::Remote(_) => None,
 912        }
 913    }
 914
 915    pub fn as_local(&self) -> Option<&LocalMode> {
 916        match &self.mode {
 917            Mode::Local(local_mode) => Some(local_mode),
 918            Mode::Remote(_) => None,
 919        }
 920    }
 921
 922    pub(super) fn initialize_sequence(
 923        &mut self,
 924        initialize_rx: oneshot::Receiver<()>,
 925        cx: &mut Context<Self>,
 926    ) -> Task<Result<()>> {
 927        match &self.mode {
 928            Mode::Local(local_mode) => {
 929                local_mode.initialize_sequence(&self.capabilities, initialize_rx, cx)
 930            }
 931            Mode::Remote(_) => Task::ready(Err(anyhow!("cannot initialize remote session"))),
 932        }
 933    }
 934
 935    pub fn output(
 936        &self,
 937        since: OutputToken,
 938    ) -> (impl Iterator<Item = &dap::OutputEvent>, OutputToken) {
 939        if self.output_token.0 == 0 {
 940            return (self.output.range(0..0), OutputToken(0));
 941        };
 942
 943        let events_since = self.output_token.0.checked_sub(since.0).unwrap_or(0);
 944
 945        let clamped_events_since = events_since.clamp(0, self.output.len());
 946        (
 947            self.output
 948                .range(self.output.len() - clamped_events_since..),
 949            self.output_token,
 950        )
 951    }
 952
 953    pub fn respond_to_client(
 954        &self,
 955        request_seq: u64,
 956        success: bool,
 957        command: String,
 958        body: Option<serde_json::Value>,
 959        cx: &mut Context<Self>,
 960    ) -> Task<Result<()>> {
 961        let Some(local_session) = self.as_local().cloned() else {
 962            unreachable!("Cannot respond to remote client");
 963        };
 964
 965        cx.background_spawn(async move {
 966            local_session
 967                .client
 968                .send_message(Message::Response(Response {
 969                    body,
 970                    success,
 971                    command,
 972                    seq: request_seq + 1,
 973                    request_seq,
 974                    message: None,
 975                }))
 976                .await
 977        })
 978    }
 979
 980    fn handle_stopped_event(&mut self, event: StoppedEvent, cx: &mut Context<Self>) {
 981        if event.all_threads_stopped.unwrap_or_default() || event.thread_id.is_none() {
 982            self.thread_states.stop_all_threads();
 983
 984            self.invalidate_command_type::<StackTraceCommand>();
 985        }
 986
 987        // Event if we stopped all threads we still need to insert the thread_id
 988        // to our own data
 989        if let Some(thread_id) = event.thread_id {
 990            self.thread_states.stop_thread(ThreadId(thread_id));
 991
 992            self.invalidate_state(
 993                &StackTraceCommand {
 994                    thread_id,
 995                    start_frame: None,
 996                    levels: None,
 997                }
 998                .into(),
 999            );
1000        }
1001
1002        self.invalidate_generic();
1003        self.threads.clear();
1004        self.variables.clear();
1005        cx.emit(SessionEvent::Stopped(
1006            event
1007                .thread_id
1008                .map(Into::into)
1009                .filter(|_| !event.preserve_focus_hint.unwrap_or(false)),
1010        ));
1011        cx.notify();
1012    }
1013
1014    pub(crate) fn handle_dap_event(&mut self, event: Box<Events>, cx: &mut Context<Self>) {
1015        match *event {
1016            Events::Initialized(_) => {
1017                debug_assert!(
1018                    false,
1019                    "Initialized event should have been handled in LocalMode"
1020                );
1021            }
1022            Events::Stopped(event) => self.handle_stopped_event(event, cx),
1023            Events::Continued(event) => {
1024                if event.all_threads_continued.unwrap_or_default() {
1025                    self.thread_states.continue_all_threads();
1026                } else {
1027                    self.thread_states
1028                        .continue_thread(ThreadId(event.thread_id));
1029                }
1030                // todo(debugger): We should be able to get away with only invalidating generic if all threads were continued
1031                self.invalidate_generic();
1032            }
1033            Events::Exited(_event) => {
1034                self.clear_active_debug_line(cx);
1035            }
1036            Events::Terminated(_) => {
1037                self.is_session_terminated = true;
1038                self.clear_active_debug_line(cx);
1039            }
1040            Events::Thread(event) => {
1041                let thread_id = ThreadId(event.thread_id);
1042
1043                match event.reason {
1044                    dap::ThreadEventReason::Started => {
1045                        self.thread_states.continue_thread(thread_id);
1046                    }
1047                    dap::ThreadEventReason::Exited => {
1048                        self.thread_states.exit_thread(thread_id);
1049                    }
1050                    reason => {
1051                        log::error!("Unhandled thread event reason {:?}", reason);
1052                    }
1053                }
1054                self.invalidate_state(&ThreadsCommand.into());
1055                cx.notify();
1056            }
1057            Events::Output(event) => {
1058                if event
1059                    .category
1060                    .as_ref()
1061                    .is_some_and(|category| *category == OutputEventCategory::Telemetry)
1062                {
1063                    return;
1064                }
1065
1066                self.output.push_back(event);
1067                self.output_token.0 += 1;
1068                cx.notify();
1069            }
1070            Events::Breakpoint(_) => {}
1071            Events::Module(event) => {
1072                match event.reason {
1073                    dap::ModuleEventReason::New => {
1074                        self.modules.push(event.module);
1075                    }
1076                    dap::ModuleEventReason::Changed => {
1077                        if let Some(module) = self
1078                            .modules
1079                            .iter_mut()
1080                            .find(|other| event.module.id == other.id)
1081                        {
1082                            *module = event.module;
1083                        }
1084                    }
1085                    dap::ModuleEventReason::Removed => {
1086                        self.modules.retain(|other| event.module.id != other.id);
1087                    }
1088                }
1089
1090                // todo(debugger): We should only send the invalidate command to downstream clients.
1091                // self.invalidate_state(&ModulesCommand.into());
1092            }
1093            Events::LoadedSource(_) => {
1094                self.invalidate_state(&LoadedSourcesCommand.into());
1095            }
1096            Events::Capabilities(event) => {
1097                self.capabilities = self.capabilities.merge(event.capabilities);
1098                cx.notify();
1099            }
1100            Events::Memory(_) => {}
1101            Events::Process(_) => {}
1102            Events::ProgressEnd(_) => {}
1103            Events::ProgressStart(_) => {}
1104            Events::ProgressUpdate(_) => {}
1105            Events::Invalidated(_) => {}
1106            Events::Other(_) => {}
1107        }
1108    }
1109
1110    /// Ensure that there's a request in flight for the given command, and if not, send it. Use this to run requests that are idempotent.
1111    fn fetch<T: DapCommand + PartialEq + Eq + Hash>(
1112        &mut self,
1113        request: T,
1114        process_result: impl FnOnce(&mut Self, Result<T::Response>, &mut Context<Self>) -> Option<T::Response>
1115            + 'static,
1116        cx: &mut Context<Self>,
1117    ) {
1118        const {
1119            assert!(
1120                T::CACHEABLE,
1121                "Only requests marked as cacheable should invoke `fetch`"
1122            );
1123        }
1124
1125        if !self.thread_states.any_stopped_thread()
1126            && request.type_id() != TypeId::of::<ThreadsCommand>()
1127            || self.is_session_terminated
1128        {
1129            return;
1130        }
1131
1132        let request_map = self
1133            .requests
1134            .entry(std::any::TypeId::of::<T>())
1135            .or_default();
1136
1137        if let Entry::Vacant(vacant) = request_map.entry(request.into()) {
1138            let command = vacant.key().0.clone().as_any_arc().downcast::<T>().unwrap();
1139
1140            let task = Self::request_inner::<Arc<T>>(
1141                &self.capabilities,
1142                self.id,
1143                &self.mode,
1144                command,
1145                process_result,
1146                cx,
1147            );
1148            let task = cx
1149                .background_executor()
1150                .spawn(async move {
1151                    let _ = task.await?;
1152                    Some(())
1153                })
1154                .shared();
1155
1156            vacant.insert(task);
1157            cx.notify();
1158        }
1159    }
1160
1161    fn request_inner<T: DapCommand + PartialEq + Eq + Hash>(
1162        capabilities: &Capabilities,
1163        session_id: SessionId,
1164        mode: &Mode,
1165        request: T,
1166        process_result: impl FnOnce(&mut Self, Result<T::Response>, &mut Context<Self>) -> Option<T::Response>
1167            + 'static,
1168        cx: &mut Context<Self>,
1169    ) -> Task<Option<T::Response>> {
1170        if !T::is_supported(&capabilities) {
1171            log::warn!(
1172                "Attempted to send a DAP request that isn't supported: {:?}",
1173                request
1174            );
1175            let error = Err(anyhow::Error::msg(
1176                "Couldn't complete request because it's not supported",
1177            ));
1178            return cx.spawn(async move |this, cx| {
1179                this.update(cx, |this, cx| process_result(this, error, cx))
1180                    .log_err()
1181                    .flatten()
1182            });
1183        }
1184
1185        let request = mode.request_dap(session_id, request, cx);
1186        cx.spawn(async move |this, cx| {
1187            let result = request.await;
1188            this.update(cx, |this, cx| process_result(this, result, cx))
1189                .log_err()
1190                .flatten()
1191        })
1192    }
1193
1194    fn request<T: DapCommand + PartialEq + Eq + Hash>(
1195        &self,
1196        request: T,
1197        process_result: impl FnOnce(&mut Self, Result<T::Response>, &mut Context<Self>) -> Option<T::Response>
1198            + 'static,
1199        cx: &mut Context<Self>,
1200    ) -> Task<Option<T::Response>> {
1201        Self::request_inner(
1202            &self.capabilities,
1203            self.id,
1204            &self.mode,
1205            request,
1206            process_result,
1207            cx,
1208        )
1209    }
1210
1211    fn invalidate_command_type<Command: DapCommand>(&mut self) {
1212        self.requests.remove(&std::any::TypeId::of::<Command>());
1213    }
1214
1215    fn invalidate_generic(&mut self) {
1216        self.invalidate_command_type::<ModulesCommand>();
1217        self.invalidate_command_type::<LoadedSourcesCommand>();
1218        self.invalidate_command_type::<ThreadsCommand>();
1219    }
1220
1221    fn invalidate_state(&mut self, key: &RequestSlot) {
1222        self.requests
1223            .entry(key.0.as_any().type_id())
1224            .and_modify(|request_map| {
1225                request_map.remove(&key);
1226            });
1227    }
1228
1229    pub fn thread_status(&self, thread_id: ThreadId) -> ThreadStatus {
1230        self.thread_states.thread_status(thread_id)
1231    }
1232
1233    pub fn threads(&mut self, cx: &mut Context<Self>) -> Vec<(dap::Thread, ThreadStatus)> {
1234        self.fetch(
1235            dap_command::ThreadsCommand,
1236            |this, result, cx| {
1237                let result = result.log_err()?;
1238
1239                this.threads = result
1240                    .iter()
1241                    .map(|thread| (ThreadId(thread.id), Thread::from(thread.clone())))
1242                    .collect();
1243
1244                this.invalidate_command_type::<StackTraceCommand>();
1245                cx.emit(SessionEvent::Threads);
1246                cx.notify();
1247
1248                Some(result)
1249            },
1250            cx,
1251        );
1252
1253        self.threads
1254            .values()
1255            .map(|thread| {
1256                (
1257                    thread.dap.clone(),
1258                    self.thread_states.thread_status(ThreadId(thread.dap.id)),
1259                )
1260            })
1261            .collect()
1262    }
1263
1264    pub fn modules(&mut self, cx: &mut Context<Self>) -> &[Module] {
1265        self.fetch(
1266            dap_command::ModulesCommand,
1267            |this, result, cx| {
1268                let result = result.log_err()?;
1269
1270                this.modules = result.iter().cloned().collect();
1271                cx.emit(SessionEvent::Modules);
1272                cx.notify();
1273
1274                Some(result)
1275            },
1276            cx,
1277        );
1278
1279        &self.modules
1280    }
1281
1282    pub fn ignore_breakpoints(&self) -> bool {
1283        self.ignore_breakpoints
1284    }
1285
1286    pub fn toggle_ignore_breakpoints(&mut self, cx: &mut App) -> Task<()> {
1287        self.set_ignore_breakpoints(!self.ignore_breakpoints, cx)
1288    }
1289
1290    pub(crate) fn set_ignore_breakpoints(&mut self, ignore: bool, cx: &mut App) -> Task<()> {
1291        if self.ignore_breakpoints == ignore {
1292            return Task::ready(());
1293        }
1294
1295        self.ignore_breakpoints = ignore;
1296
1297        if let Some(local) = self.as_local() {
1298            local.send_all_breakpoints(ignore, cx)
1299        } else {
1300            // todo(debugger): We need to propagate this change to downstream sessions and send a message to upstream sessions
1301            unimplemented!()
1302        }
1303    }
1304
1305    pub fn breakpoints_enabled(&self) -> bool {
1306        self.ignore_breakpoints
1307    }
1308
1309    pub fn loaded_sources(&mut self, cx: &mut Context<Self>) -> &[Source] {
1310        self.fetch(
1311            dap_command::LoadedSourcesCommand,
1312            |this, result, cx| {
1313                let result = result.log_err()?;
1314                this.loaded_sources = result.iter().cloned().collect();
1315                cx.emit(SessionEvent::LoadedSources);
1316                cx.notify();
1317                Some(result)
1318            },
1319            cx,
1320        );
1321
1322        &self.loaded_sources
1323    }
1324
1325    fn empty_response(&mut self, res: Result<()>, _cx: &mut Context<Self>) -> Option<()> {
1326        res.log_err()?;
1327        Some(())
1328    }
1329
1330    fn on_step_response<T: DapCommand + PartialEq + Eq + Hash>(
1331        thread_id: ThreadId,
1332    ) -> impl FnOnce(&mut Self, Result<T::Response>, &mut Context<Self>) -> Option<T::Response> + 'static
1333    {
1334        move |this, response, cx| match response.log_err() {
1335            Some(response) => Some(response),
1336            None => {
1337                this.thread_states.stop_thread(thread_id);
1338                cx.notify();
1339                None
1340            }
1341        }
1342    }
1343
1344    fn clear_active_debug_line_response(
1345        &mut self,
1346        response: Result<()>,
1347        cx: &mut Context<'_, Session>,
1348    ) -> Option<()> {
1349        response.log_err()?;
1350        self.clear_active_debug_line(cx);
1351        Some(())
1352    }
1353
1354    fn clear_active_debug_line(&mut self, cx: &mut Context<Session>) {
1355        self.as_local()
1356            .expect("Message handler will only run in local mode")
1357            .breakpoint_store
1358            .update(cx, |store, cx| {
1359                store.remove_active_position(Some(self.id), cx)
1360            });
1361    }
1362
1363    pub fn pause_thread(&mut self, thread_id: ThreadId, cx: &mut Context<Self>) {
1364        self.request(
1365            PauseCommand {
1366                thread_id: thread_id.0,
1367            },
1368            Self::empty_response,
1369            cx,
1370        )
1371        .detach();
1372    }
1373
1374    pub fn restart_stack_frame(&mut self, stack_frame_id: u64, cx: &mut Context<Self>) {
1375        self.request(
1376            RestartStackFrameCommand { stack_frame_id },
1377            Self::empty_response,
1378            cx,
1379        )
1380        .detach();
1381    }
1382
1383    pub fn restart(&mut self, args: Option<Value>, cx: &mut Context<Self>) {
1384        if self.capabilities.supports_restart_request.unwrap_or(false) {
1385            self.request(
1386                RestartCommand {
1387                    raw: args.unwrap_or(Value::Null),
1388                },
1389                Self::empty_response,
1390                cx,
1391            )
1392            .detach();
1393        } else {
1394            self.request(
1395                DisconnectCommand {
1396                    restart: Some(false),
1397                    terminate_debuggee: Some(true),
1398                    suspend_debuggee: Some(false),
1399                },
1400                Self::empty_response,
1401                cx,
1402            )
1403            .detach();
1404        }
1405    }
1406
1407    pub fn shutdown(&mut self, cx: &mut Context<Self>) -> Task<()> {
1408        self.is_session_terminated = true;
1409        self.thread_states.exit_all_threads();
1410        cx.notify();
1411
1412        let task = if self
1413            .capabilities
1414            .supports_terminate_request
1415            .unwrap_or_default()
1416        {
1417            self.request(
1418                TerminateCommand {
1419                    restart: Some(false),
1420                },
1421                Self::clear_active_debug_line_response,
1422                cx,
1423            )
1424        } else {
1425            self.request(
1426                DisconnectCommand {
1427                    restart: Some(false),
1428                    terminate_debuggee: Some(true),
1429                    suspend_debuggee: Some(false),
1430                },
1431                Self::clear_active_debug_line_response,
1432                cx,
1433            )
1434        };
1435
1436        cx.background_spawn(async move {
1437            let _ = task.await;
1438        })
1439    }
1440
1441    pub fn completions(
1442        &mut self,
1443        query: CompletionsQuery,
1444        cx: &mut Context<Self>,
1445    ) -> Task<Result<Vec<dap::CompletionItem>>> {
1446        let task = self.request(query, |_, result, _| result.log_err(), cx);
1447
1448        cx.background_executor().spawn(async move {
1449            anyhow::Ok(
1450                task.await
1451                    .map(|response| response.targets)
1452                    .ok_or_else(|| anyhow!("failed to fetch completions"))?,
1453            )
1454        })
1455    }
1456
1457    pub fn continue_thread(&mut self, thread_id: ThreadId, cx: &mut Context<Self>) {
1458        self.thread_states.continue_thread(thread_id);
1459        self.request(
1460            ContinueCommand {
1461                args: ContinueArguments {
1462                    thread_id: thread_id.0,
1463                    single_thread: Some(true),
1464                },
1465            },
1466            Self::on_step_response::<ContinueCommand>(thread_id),
1467            cx,
1468        )
1469        .detach();
1470    }
1471
1472    pub fn adapter_client(&self) -> Option<Arc<DebugAdapterClient>> {
1473        match self.mode {
1474            Mode::Local(ref local) => Some(local.client.clone()),
1475            Mode::Remote(_) => None,
1476        }
1477    }
1478
1479    pub fn step_over(
1480        &mut self,
1481        thread_id: ThreadId,
1482        granularity: SteppingGranularity,
1483        cx: &mut Context<Self>,
1484    ) {
1485        let supports_single_thread_execution_requests =
1486            self.capabilities.supports_single_thread_execution_requests;
1487        let supports_stepping_granularity = self
1488            .capabilities
1489            .supports_stepping_granularity
1490            .unwrap_or_default();
1491
1492        let command = NextCommand {
1493            inner: StepCommand {
1494                thread_id: thread_id.0,
1495                granularity: supports_stepping_granularity.then(|| granularity),
1496                single_thread: supports_single_thread_execution_requests,
1497            },
1498        };
1499
1500        self.thread_states.process_step(thread_id);
1501        self.request(
1502            command,
1503            Self::on_step_response::<NextCommand>(thread_id),
1504            cx,
1505        )
1506        .detach();
1507    }
1508
1509    pub fn step_in(
1510        &mut self,
1511        thread_id: ThreadId,
1512        granularity: SteppingGranularity,
1513        cx: &mut Context<Self>,
1514    ) {
1515        let supports_single_thread_execution_requests =
1516            self.capabilities.supports_single_thread_execution_requests;
1517        let supports_stepping_granularity = self
1518            .capabilities
1519            .supports_stepping_granularity
1520            .unwrap_or_default();
1521
1522        let command = StepInCommand {
1523            inner: StepCommand {
1524                thread_id: thread_id.0,
1525                granularity: supports_stepping_granularity.then(|| granularity),
1526                single_thread: supports_single_thread_execution_requests,
1527            },
1528        };
1529
1530        self.thread_states.process_step(thread_id);
1531        self.request(
1532            command,
1533            Self::on_step_response::<StepInCommand>(thread_id),
1534            cx,
1535        )
1536        .detach();
1537    }
1538
1539    pub fn step_out(
1540        &mut self,
1541        thread_id: ThreadId,
1542        granularity: SteppingGranularity,
1543        cx: &mut Context<Self>,
1544    ) {
1545        let supports_single_thread_execution_requests =
1546            self.capabilities.supports_single_thread_execution_requests;
1547        let supports_stepping_granularity = self
1548            .capabilities
1549            .supports_stepping_granularity
1550            .unwrap_or_default();
1551
1552        let command = StepOutCommand {
1553            inner: StepCommand {
1554                thread_id: thread_id.0,
1555                granularity: supports_stepping_granularity.then(|| granularity),
1556                single_thread: supports_single_thread_execution_requests,
1557            },
1558        };
1559
1560        self.thread_states.process_step(thread_id);
1561        self.request(
1562            command,
1563            Self::on_step_response::<StepOutCommand>(thread_id),
1564            cx,
1565        )
1566        .detach();
1567    }
1568
1569    pub fn step_back(
1570        &mut self,
1571        thread_id: ThreadId,
1572        granularity: SteppingGranularity,
1573        cx: &mut Context<Self>,
1574    ) {
1575        let supports_single_thread_execution_requests =
1576            self.capabilities.supports_single_thread_execution_requests;
1577        let supports_stepping_granularity = self
1578            .capabilities
1579            .supports_stepping_granularity
1580            .unwrap_or_default();
1581
1582        let command = StepBackCommand {
1583            inner: StepCommand {
1584                thread_id: thread_id.0,
1585                granularity: supports_stepping_granularity.then(|| granularity),
1586                single_thread: supports_single_thread_execution_requests,
1587            },
1588        };
1589
1590        self.thread_states.process_step(thread_id);
1591
1592        self.request(
1593            command,
1594            Self::on_step_response::<StepBackCommand>(thread_id),
1595            cx,
1596        )
1597        .detach();
1598    }
1599
1600    pub fn stack_frames(&mut self, thread_id: ThreadId, cx: &mut Context<Self>) -> Vec<StackFrame> {
1601        if self.thread_states.thread_status(thread_id) == ThreadStatus::Stopped
1602            && self.requests.contains_key(&ThreadsCommand.type_id())
1603            && self.threads.contains_key(&thread_id)
1604        // ^ todo(debugger): We need a better way to check that we're not querying stale data
1605        // We could still be using an old thread id and have sent a new thread's request
1606        // This isn't the biggest concern right now because it hasn't caused any issues outside of tests
1607        // But it very well could cause a minor bug in the future that is hard to track down
1608        {
1609            self.fetch(
1610                super::dap_command::StackTraceCommand {
1611                    thread_id: thread_id.0,
1612                    start_frame: None,
1613                    levels: None,
1614                },
1615                move |this, stack_frames, cx| {
1616                    let stack_frames = stack_frames.log_err()?;
1617
1618                    let entry = this.threads.entry(thread_id).and_modify(|thread| {
1619                        thread.stack_frame_ids =
1620                            stack_frames.iter().map(|frame| frame.id).collect();
1621                    });
1622                    debug_assert!(
1623                        matches!(entry, indexmap::map::Entry::Occupied(_)),
1624                        "Sent request for thread_id that doesn't exist"
1625                    );
1626
1627                    this.stack_frames.extend(
1628                        stack_frames
1629                            .iter()
1630                            .cloned()
1631                            .map(|frame| (frame.id, StackFrame::from(frame))),
1632                    );
1633
1634                    this.invalidate_command_type::<ScopesCommand>();
1635                    this.invalidate_command_type::<VariablesCommand>();
1636
1637                    cx.emit(SessionEvent::StackTrace);
1638                    cx.notify();
1639                    Some(stack_frames)
1640                },
1641                cx,
1642            );
1643        }
1644
1645        self.threads
1646            .get(&thread_id)
1647            .map(|thread| {
1648                thread
1649                    .stack_frame_ids
1650                    .iter()
1651                    .filter_map(|id| self.stack_frames.get(id))
1652                    .cloned()
1653                    .collect()
1654            })
1655            .unwrap_or_default()
1656    }
1657
1658    pub fn scopes(&mut self, stack_frame_id: u64, cx: &mut Context<Self>) -> &[dap::Scope] {
1659        if self.requests.contains_key(&TypeId::of::<ThreadsCommand>())
1660            && self
1661                .requests
1662                .contains_key(&TypeId::of::<StackTraceCommand>())
1663        {
1664            self.fetch(
1665                ScopesCommand { stack_frame_id },
1666                move |this, scopes, cx| {
1667                    let scopes = scopes.log_err()?;
1668
1669                    for scope in scopes .iter(){
1670                        this.variables(scope.variables_reference, cx);
1671                    }
1672
1673                    let entry = this
1674                        .stack_frames
1675                        .entry(stack_frame_id)
1676                        .and_modify(|stack_frame| {
1677                            stack_frame.scopes = scopes.clone();
1678                        });
1679
1680                    cx.emit(SessionEvent::Variables);
1681
1682                    debug_assert!(
1683                        matches!(entry, indexmap::map::Entry::Occupied(_)),
1684                        "Sent scopes request for stack_frame_id that doesn't exist or hasn't been fetched"
1685                    );
1686
1687                    Some(scopes)
1688                },
1689                cx,
1690            );
1691        }
1692
1693        self.stack_frames
1694            .get(&stack_frame_id)
1695            .map(|frame| frame.scopes.as_slice())
1696            .unwrap_or_default()
1697    }
1698
1699    pub fn variables(
1700        &mut self,
1701        variables_reference: VariableReference,
1702        cx: &mut Context<Self>,
1703    ) -> Vec<dap::Variable> {
1704        let command = VariablesCommand {
1705            variables_reference,
1706            filter: None,
1707            start: None,
1708            count: None,
1709            format: None,
1710        };
1711
1712        self.fetch(
1713            command,
1714            move |this, variables, cx| {
1715                let variables = variables.log_err()?;
1716                this.variables
1717                    .insert(variables_reference, variables.clone());
1718
1719                cx.emit(SessionEvent::Variables);
1720                Some(variables)
1721            },
1722            cx,
1723        );
1724
1725        self.variables
1726            .get(&variables_reference)
1727            .cloned()
1728            .unwrap_or_default()
1729    }
1730
1731    pub fn set_variable_value(
1732        &mut self,
1733        variables_reference: u64,
1734        name: String,
1735        value: String,
1736        cx: &mut Context<Self>,
1737    ) {
1738        if self.capabilities.supports_set_variable.unwrap_or_default() {
1739            self.request(
1740                SetVariableValueCommand {
1741                    name,
1742                    value,
1743                    variables_reference,
1744                },
1745                move |this, response, cx| {
1746                    let response = response.log_err()?;
1747                    this.invalidate_command_type::<VariablesCommand>();
1748                    cx.notify();
1749                    Some(response)
1750                },
1751                cx,
1752            )
1753            .detach()
1754        }
1755    }
1756
1757    pub fn evaluate(
1758        &mut self,
1759        expression: String,
1760        context: Option<EvaluateArgumentsContext>,
1761        frame_id: Option<u64>,
1762        source: Option<Source>,
1763        cx: &mut Context<Self>,
1764    ) {
1765        self.request(
1766            EvaluateCommand {
1767                expression,
1768                context,
1769                frame_id,
1770                source,
1771            },
1772            |this, response, cx| {
1773                let response = response.log_err()?;
1774                this.output_token.0 += 1;
1775                this.output.push_back(dap::OutputEvent {
1776                    category: None,
1777                    output: response.result.clone(),
1778                    group: None,
1779                    variables_reference: Some(response.variables_reference),
1780                    source: None,
1781                    line: None,
1782                    column: None,
1783                    data: None,
1784                    location_reference: None,
1785                });
1786
1787                this.invalidate_command_type::<ScopesCommand>();
1788                cx.notify();
1789                Some(response)
1790            },
1791            cx,
1792        )
1793        .detach();
1794    }
1795
1796    pub fn location(
1797        &mut self,
1798        reference: u64,
1799        cx: &mut Context<Self>,
1800    ) -> Option<dap::LocationsResponse> {
1801        self.fetch(
1802            LocationsCommand { reference },
1803            move |this, response, _| {
1804                let response = response.log_err()?;
1805                this.locations.insert(reference, response.clone());
1806                Some(response)
1807            },
1808            cx,
1809        );
1810        self.locations.get(&reference).cloned()
1811    }
1812    pub fn disconnect_client(&mut self, cx: &mut Context<Self>) {
1813        let command = DisconnectCommand {
1814            restart: Some(false),
1815            terminate_debuggee: Some(true),
1816            suspend_debuggee: Some(false),
1817        };
1818
1819        self.request(command, Self::empty_response, cx).detach()
1820    }
1821
1822    pub fn terminate_threads(&mut self, thread_ids: Option<Vec<ThreadId>>, cx: &mut Context<Self>) {
1823        if self
1824            .capabilities
1825            .supports_terminate_threads_request
1826            .unwrap_or_default()
1827        {
1828            self.request(
1829                TerminateThreadsCommand {
1830                    thread_ids: thread_ids.map(|ids| ids.into_iter().map(|id| id.0).collect()),
1831                },
1832                Self::clear_active_debug_line_response,
1833                cx,
1834            )
1835            .detach();
1836        } else {
1837            self.shutdown(cx).detach();
1838        }
1839    }
1840}