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