session.rs

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