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