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