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