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