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