1use std::sync::Arc;
42
43use CommonLibrary::{
44 Debug::DebugService::DebugService,
45 Environment::Requires::Requires,
46 Error::CommonError::CommonError,
47 IPC::{DTO::ProxyTarget::ProxyTarget, IPCProvider::IPCProvider},
48};
49use async_trait::async_trait;
50use serde_json::{Value, json};
51use tauri::Emitter;
52use url::Url;
53
54use super::MountainEnvironment::MountainEnvironment;
55use crate::dev_log;
56
57#[async_trait]
58impl DebugService for MountainEnvironment {
59 async fn RegisterDebugConfigurationProvider(
60 &self,
61
62 DebugType:String,
63
64 ProviderHandle:u32,
65
66 SideCarIdentifier:String,
67 ) -> Result<(), CommonError> {
68 if DebugType.is_empty() {
70 return Err(CommonError::InvalidArgument {
71 ArgumentName:"DebugType".to_string(),
72 Reason:"DebugType cannot be empty".to_string(),
73 });
74 }
75
76 dev_log!(
77 "exthost",
78 "[DebugProvider] Registering DebugConfigurationProvider for type '{}' (handle: {}, sidecar: {})",
79 DebugType,
80 ProviderHandle,
81 SideCarIdentifier
82 );
83
84 self.ApplicationState
86 .Feature
87 .Debug
88 .RegisterDebugConfigurationProvider(DebugType, ProviderHandle, SideCarIdentifier)
89 .map_err(|e| CommonError::Unknown { Description:e })?;
90
91 Ok(())
92 }
93
94 async fn RegisterDebugAdapterDescriptorFactory(
95 &self,
96
97 DebugType:String,
98
99 FactoryHandle:u32,
100
101 SideCarIdentifier:String,
102 ) -> Result<(), CommonError> {
103 if DebugType.is_empty() {
105 return Err(CommonError::InvalidArgument {
106 ArgumentName:"DebugType".to_string(),
107 Reason:"DebugType cannot be empty".to_string(),
108 });
109 }
110
111 dev_log!(
112 "exthost",
113 "[DebugProvider] Registering DebugAdapterDescriptorFactory for type '{}' (handle: {}, sidecar: {})",
114 DebugType,
115 FactoryHandle,
116 SideCarIdentifier
117 );
118
119 self.ApplicationState
121 .Feature
122 .Debug
123 .RegisterDebugAdapterDescriptorFactory(DebugType, FactoryHandle, SideCarIdentifier)
124 .map_err(|e| CommonError::Unknown { Description:e })?;
125
126 Ok(())
127 }
128
129 async fn StartDebugging(&self, _FolderURI:Option<Url>, Configuration:Value) -> Result<String, CommonError> {
130 let SessionID = uuid::Uuid::new_v4().to_string();
131
132 dev_log!(
133 "exthost",
134 "[DebugProvider] Starting debug session '{}' with config: {:?}",
135 SessionID,
136 Configuration
137 );
138
139 let IPCProvider:Arc<dyn IPCProvider> = self.Require();
140
141 let DebugType = Configuration
142 .get("type")
143 .and_then(Value::as_str)
144 .ok_or_else(|| {
145 CommonError::InvalidArgument {
146 ArgumentName:"Configuration".into(),
147
148 Reason:"Missing 'type' field in debug configuration.".into(),
149 }
150 })?
151 .to_string();
152
153 let TargetSideCar = self
157 .ApplicationState
158 .Feature
159 .Debug
160 .GetDebugConfigurationProvider(&DebugType)
161 .map(|R| R.SideCarIdentifier.clone())
162 .unwrap_or_else(|| "cocoon-main".to_string());
163
164 dev_log!(
166 "exthost",
167 "[DebugProvider] Resolving debug configuration for type '{}'",
168 DebugType
169 );
170
171 dev_log!("exthost", "[DebugProvider] Resolving debug configuration...");
172
173 let ResolveConfigMethod = format!("{}$resolveDebugConfiguration", ProxyTarget::ExtHostDebug.GetTargetPrefix());
174
175 let ResolvedConfig = IPCProvider
176 .SendRequestToSideCar(
177 TargetSideCar.clone(),
178 ResolveConfigMethod,
179 json!([DebugType.clone(), Configuration]),
180 5000,
181 )
182 .await?;
183
184 dev_log!("exthost", "[DebugProvider] Creating debug adapter descriptor...");
186
187 let CreateDescriptorMethod =
188 format!("{}$createDebugAdapterDescriptor", ProxyTarget::ExtHostDebug.GetTargetPrefix());
189
190 let Descriptor = IPCProvider
191 .SendRequestToSideCar(
192 TargetSideCar.clone(),
193 CreateDescriptorMethod,
194 json!([DebugType, &ResolvedConfig]),
195 5000,
196 )
197 .await?;
198
199 dev_log!(
201 "exthost",
202 "[DebugProvider] Spawning Debug Adapter based on descriptor: {:?}",
203 Descriptor
204 );
205
206 let DescriptorType = Descriptor.get("type").and_then(Value::as_str).unwrap_or("").to_string();
223
224 let AdapterStdinSender:Option<tokio::sync::mpsc::UnboundedSender<Vec<u8>>>;
225
226 let AdapterChildPid:Option<u32>;
227
228 match DescriptorType.as_str() {
229 "executable" => {
230 let Command = Descriptor
231 .get("command")
232 .and_then(Value::as_str)
233 .ok_or_else(|| {
234 CommonError::InvalidArgument {
235 ArgumentName:"Descriptor.command".into(),
236 Reason:"executable adapter descriptor missing 'command'".into(),
237 }
238 })?
239 .to_string();
240
241 let Args:Vec<String> = Descriptor
242 .get("args")
243 .and_then(Value::as_array)
244 .map(|A| A.iter().filter_map(|V| V.as_str().map(str::to_string)).collect())
245 .unwrap_or_default();
246
247 let OptionsValue = Descriptor.get("options").cloned().unwrap_or(Value::Null);
248
249 let Cwd = OptionsValue.get("cwd").and_then(Value::as_str).map(str::to_string);
250
251 let EnvOverrides:Vec<(String, String)> = OptionsValue
252 .get("env")
253 .and_then(Value::as_object)
254 .map(|O| {
255 O.iter()
256 .filter_map(|(K, V)| V.as_str().map(|S| (K.clone(), S.to_string())))
257 .collect()
258 })
259 .unwrap_or_default();
260
261 let mut Builder = tokio::process::Command::new(&Command);
262
263 Builder
264 .args(&Args)
265 .stdin(std::process::Stdio::piped())
266 .stdout(std::process::Stdio::piped())
267 .stderr(std::process::Stdio::piped());
268
269 if let Some(CwdPath) = &Cwd {
270 Builder.current_dir(CwdPath);
271 }
272
273 for (Key, Value) in &EnvOverrides {
274 Builder.env(Key, Value);
275 }
276
277 let mut Child = Builder.spawn().map_err(|Error| {
278 CommonError::IPCError {
279 Description:format!(
280 "Failed to spawn debug adapter '{}' for session {}: {}",
281 Command, SessionID, Error
282 ),
283 }
284 })?;
285
286 let Pid = Child.id();
287
288 let Stdin = Child.stdin.take().ok_or_else(|| {
289 CommonError::IPCError { Description:format!("Adapter for session {} had no stdin pipe", SessionID) }
290 })?;
291
292 let Stdout = Child.stdout.take().ok_or_else(|| {
293 CommonError::IPCError {
294 Description:format!("Adapter for session {} had no stdout pipe", SessionID),
295 }
296 })?;
297
298 let Stderr = Child.stderr.take().ok_or_else(|| {
299 CommonError::IPCError {
300 Description:format!("Adapter for session {} had no stderr pipe", SessionID),
301 }
302 })?;
303
304 let (Sender, mut Receiver) = tokio::sync::mpsc::unbounded_channel::<Vec<u8>>();
305
306 let StdinSessionId = SessionID.clone();
311
312 tokio::spawn(async move {
313 use tokio::io::AsyncWriteExt;
314
315 let mut Pipe = Stdin;
316
317 while let Some(Frame) = Receiver.recv().await {
318 if let Err(Error) = Pipe.write_all(&Frame).await {
319 crate::dev_log!(
320 "exthost",
321 "warn: [DebugAdapter] stdin write failed for session {}: {}",
322 StdinSessionId,
323 Error
324 );
325
326 break;
327 }
328
329 if let Err(Error) = Pipe.flush().await {
330 crate::dev_log!(
331 "exthost",
332 "warn: [DebugAdapter] stdin flush failed for session {}: {}",
333 StdinSessionId,
334 Error
335 );
336
337 break;
338 }
339 }
340
341 let _ = Pipe.shutdown().await;
342 });
343
344 let StdoutSessionId = SessionID.clone();
351
352 let StdoutHandle = self.ApplicationHandle.clone();
353
354 let StdoutSidecar = TargetSideCar.clone();
355
356 tokio::spawn(async move {
357 use tokio::io::{AsyncBufReadExt, AsyncReadExt, BufReader};
358
359 let mut Reader = BufReader::new(Stdout);
360
361 let mut Header = String::new();
362
363 loop {
364 Header.clear();
365
366 let mut ContentLength:usize = 0;
367
368 loop {
369 Header.clear();
370
371 match Reader.read_line(&mut Header).await {
372 Ok(0) => return, Ok(_) => {},
374 Err(Error) => {
375 crate::dev_log!(
376 "exthost",
377 "warn: [DebugAdapter] stdout read failed for session {}: {}",
378 StdoutSessionId,
379 Error
380 );
381
382 return;
383 },
384 }
385
386 let Trimmed = Header.trim_end_matches("\r\n").trim_end_matches('\n');
387
388 if Trimmed.is_empty() {
389 break;
390 }
391
392 if let Some(Rest) = Trimmed.strip_prefix("Content-Length:") {
393 if let Ok(N) = Rest.trim().parse::<usize>() {
394 ContentLength = N;
395 }
396 }
397 }
398
399 if ContentLength == 0 {
400 continue;
401 }
402
403 let mut Body = vec![0u8; ContentLength];
404
405 if let Err(Error) = Reader.read_exact(&mut Body).await {
406 crate::dev_log!(
407 "exthost",
408 "warn: [DebugAdapter] stdout body read failed for session {}: {}",
409 StdoutSessionId,
410 Error
411 );
412
413 return;
414 }
415
416 let Parsed:Value = serde_json::from_slice(&Body).unwrap_or(Value::Null);
417
418 let _ = StdoutHandle.emit(
419 "sky://debug/dap-message",
420 json!({
421 "sessionId": StdoutSessionId,
422 "sidecarId": StdoutSidecar,
423 "message": Parsed,
424 }),
425 );
426 }
427 });
428
429 let StderrSessionId = SessionID.clone();
432
433 tokio::spawn(async move {
434 use tokio::io::{AsyncBufReadExt, BufReader};
435
436 let mut Lines = BufReader::new(Stderr).lines();
437
438 while let Ok(Some(Line)) = Lines.next_line().await {
439 crate::dev_log!("exthost", "[DebugAdapter] stderr session={}: {}", StderrSessionId, Line);
440 }
441 });
442
443 AdapterStdinSender = Some(Sender);
444
445 AdapterChildPid = Pid;
446
447 dev_log!(
448 "exthost",
449 "[DebugProvider] Spawned executable adapter for session '{}' pid={:?} command={:?}",
450 SessionID,
451 Pid,
452 Command
453 );
454 },
455
456 "server" => {
457 let Port = Descriptor.get("port").and_then(Value::as_u64).ok_or_else(|| {
462 CommonError::InvalidArgument {
463 ArgumentName:"Descriptor.port".into(),
464 Reason:"server adapter descriptor missing 'port'".into(),
465 }
466 })? as u16;
467
468 let Host = Descriptor
469 .get("host")
470 .and_then(Value::as_str)
471 .unwrap_or("127.0.0.1")
472 .to_string();
473
474 let Addr = format!("{}:{}", Host, Port);
475
476 dev_log!(
477 "exthost",
478 "[DebugProvider] Connecting to debug adapter server at {} (session '{}')",
479 Addr,
480 SessionID
481 );
482
483 let TcpStream = tokio::net::TcpStream::connect(&Addr).await.map_err(|Error| {
484 CommonError::IPCError {
485 Description:format!(
486 "Failed to connect to debug adapter server at {} for session {}: {}",
487 Addr, SessionID, Error
488 ),
489 }
490 })?;
491
492 let (ReadHalf, WriteHalf) = tokio::io::split(TcpStream);
493
494 let (Sender, mut Receiver) = tokio::sync::mpsc::unbounded_channel::<Vec<u8>>();
495
496 let WriterSessionId = SessionID.clone();
498
499 tokio::spawn(async move {
500 use tokio::io::AsyncWriteExt;
501
502 let mut Pipe = WriteHalf;
503
504 while let Some(Frame) = Receiver.recv().await {
505 if let Err(Error) = Pipe.write_all(&Frame).await {
506 crate::dev_log!(
507 "exthost",
508 "warn: [DebugAdapter/server] write failed for session {}: {}",
509 WriterSessionId,
510 Error
511 );
512
513 break;
514 }
515
516 let _ = Pipe.flush().await;
517 }
518 });
519
520 let ReaderSessionId = SessionID.clone();
523
524 let ReaderHandle = self.ApplicationHandle.clone();
525
526 let ReaderSidecar = TargetSideCar.clone();
527
528 tokio::spawn(async move {
529 use tokio::io::{AsyncBufReadExt, AsyncReadExt, BufReader};
530
531 let mut Reader = BufReader::new(ReadHalf);
532
533 let mut Header = String::new();
534
535 loop {
536 Header.clear();
537
538 let mut ContentLength:usize = 0;
539
540 loop {
541 Header.clear();
542
543 match Reader.read_line(&mut Header).await {
544 Ok(0) => return,
545 Ok(_) => {},
546 Err(Error) => {
547 crate::dev_log!(
548 "exthost",
549 "warn: [DebugAdapter/server] read failed for session {}: {}",
550 ReaderSessionId,
551 Error
552 );
553
554 return;
555 },
556 }
557
558 let Trimmed = Header.trim_end_matches("\r\n").trim_end_matches('\n');
559
560 if Trimmed.is_empty() {
561 break;
562 }
563
564 if let Some(Rest) = Trimmed.strip_prefix("Content-Length:") {
565 if let Ok(N) = Rest.trim().parse::<usize>() {
566 ContentLength = N;
567 }
568 }
569 }
570
571 if ContentLength == 0 {
572 continue;
573 }
574
575 let mut Body = vec![0u8; ContentLength];
576
577 if let Err(Error) = Reader.read_exact(&mut Body).await {
578 crate::dev_log!(
579 "exthost",
580 "warn: [DebugAdapter/server] body read failed for session {}: {}",
581 ReaderSessionId,
582 Error
583 );
584
585 return;
586 }
587
588 let Parsed:Value = serde_json::from_slice(&Body).unwrap_or(Value::Null);
589
590 let _ = ReaderHandle.emit(
591 "sky://debug/dap-message",
592 json!({
593 "sessionId": ReaderSessionId,
594 "sidecarId": ReaderSidecar,
595 "message": Parsed,
596 }),
597 );
598 }
599 });
600
601 AdapterStdinSender = Some(Sender);
602
603 AdapterChildPid = None;
604
605 dev_log!(
606 "exthost",
607 "[DebugProvider] Connected to server adapter at {} for session '{}'",
608 Addr,
609 SessionID
610 );
611 },
612
613 "pipeServer" => {
614 let PipePath = Descriptor
618 .get("path")
619 .and_then(Value::as_str)
620 .ok_or_else(|| {
621 CommonError::InvalidArgument {
622 ArgumentName:"Descriptor.path".into(),
623 Reason:"pipeServer adapter descriptor missing 'path'".into(),
624 }
625 })?
626 .to_string();
627
628 dev_log!(
629 "exthost",
630 "[DebugProvider] Connecting to debug adapter pipe at '{}' (session '{}')",
631 PipePath,
632 SessionID
633 );
634
635 #[cfg(unix)]
636 let (ReadHalf, WriteHalf) = {
637 let Stream = tokio::net::UnixStream::connect(&PipePath).await.map_err(|Error| {
638 CommonError::IPCError {
639 Description:format!(
640 "Failed to connect to debug adapter pipe '{}' for session {}: {}",
641 PipePath, SessionID, Error
642 ),
643 }
644 })?;
645
646 tokio::io::split(Stream)
647 };
648
649 #[cfg(windows)]
650 let (ReadHalf, WriteHalf) = {
651 let Stream =
654 tokio::net::windows::named_pipe::ClientOptions::new()
655 .open(&PipePath)
656 .map_err(|Error| {
657 CommonError::IPCError {
658 Description:format!(
659 "Failed to open named pipe '{}' for session {}: {}",
660 PipePath, SessionID, Error
661 ),
662 }
663 })?;
664
665 tokio::io::split(Stream)
666 };
667
668 let (Sender, mut Receiver) = tokio::sync::mpsc::unbounded_channel::<Vec<u8>>();
669
670 let PipeWriterSessionId = SessionID.clone();
671
672 tokio::spawn(async move {
673 use tokio::io::AsyncWriteExt;
674
675 let mut Pipe = WriteHalf;
676
677 while let Some(Frame) = Receiver.recv().await {
678 if let Err(Error) = Pipe.write_all(&Frame).await {
679 crate::dev_log!(
680 "exthost",
681 "warn: [DebugAdapter/pipe] write failed for session {}: {}",
682 PipeWriterSessionId,
683 Error
684 );
685
686 break;
687 }
688
689 let _ = Pipe.flush().await;
690 }
691 });
692
693 let PipeReaderSessionId = SessionID.clone();
694
695 let PipeReaderHandle = self.ApplicationHandle.clone();
696
697 let PipeReaderSidecar = TargetSideCar.clone();
698
699 tokio::spawn(async move {
700 use tokio::io::{AsyncBufReadExt, AsyncReadExt, BufReader};
701
702 let mut Reader = BufReader::new(ReadHalf);
703
704 let mut Header = String::new();
705
706 loop {
707 Header.clear();
708
709 let mut ContentLength:usize = 0;
710
711 loop {
712 Header.clear();
713
714 match Reader.read_line(&mut Header).await {
715 Ok(0) => return,
716 Ok(_) => {},
717 Err(Error) => {
718 crate::dev_log!(
719 "exthost",
720 "warn: [DebugAdapter/pipe] read failed for session {}: {}",
721 PipeReaderSessionId,
722 Error
723 );
724
725 return;
726 },
727 }
728
729 let Trimmed = Header.trim_end_matches("\r\n").trim_end_matches('\n');
730
731 if Trimmed.is_empty() {
732 break;
733 }
734
735 if let Some(Rest) = Trimmed.strip_prefix("Content-Length:") {
736 if let Ok(N) = Rest.trim().parse::<usize>() {
737 ContentLength = N;
738 }
739 }
740 }
741
742 if ContentLength == 0 {
743 continue;
744 }
745
746 let mut Body = vec![0u8; ContentLength];
747
748 if let Err(Error) = Reader.read_exact(&mut Body).await {
749 crate::dev_log!(
750 "exthost",
751 "warn: [DebugAdapter/pipe] body read failed for session {}: {}",
752 PipeReaderSessionId,
753 Error
754 );
755
756 return;
757 }
758
759 let Parsed:Value = serde_json::from_slice(&Body).unwrap_or(Value::Null);
760
761 let _ = PipeReaderHandle.emit(
762 "sky://debug/dap-message",
763 json!({
764 "sessionId": PipeReaderSessionId,
765 "sidecarId": PipeReaderSidecar,
766 "message": Parsed,
767 }),
768 );
769 }
770 });
771
772 AdapterStdinSender = Some(Sender);
773
774 AdapterChildPid = None;
775
776 dev_log!(
777 "exthost",
778 "[DebugProvider] Connected to pipe adapter at '{}' for session '{}'",
779 PipePath,
780 SessionID
781 );
782 },
783
784 "implementation" => {
785 dev_log!(
786 "exthost",
787 "[DebugProvider] Inline implementation adapter for session '{}' - DAP frames travel via Cocoon \
788 reverse-RPC.",
789 SessionID
790 );
791
792 AdapterStdinSender = None;
793
794 AdapterChildPid = None;
795 },
796
797 _ => {
798 dev_log!(
799 "exthost",
800 "warn: [DebugProvider] Unknown adapter descriptor type '{}' for session '{}' - registering \
801 session without spawn.",
802 DescriptorType,
803 SessionID
804 );
805
806 AdapterStdinSender = None;
807
808 AdapterChildPid = None;
809 },
810 }
811
812 if let Err(RegError) = self.ApplicationState.Feature.Debug.RegisterDebugSession(
817 crate::ApplicationState::State::FeatureState::Debug::DebugState::DebugSessionEntry {
818 SessionId:SessionID.clone(),
819 DebugType:DebugType.clone(),
820 SideCarIdentifier:TargetSideCar.clone(),
821 StdinSender:AdapterStdinSender,
822 ChildPid:AdapterChildPid,
823 },
824 ) {
825 dev_log!(
826 "exthost",
827 "warn: [DebugProvider] Failed to register session '{}' in DebugState: {}",
828 SessionID,
829 RegError
830 );
831 }
832
833 let StartedMethod = format!("{}$onDidStartDebugSession", ProxyTarget::ExtHostDebug.GetTargetPrefix());
843
844 let StartedSession = json!({
845 "id": SessionID.clone(),
846 "type": DebugType.clone(),
847 "name": ResolvedConfig.get("name").and_then(Value::as_str).unwrap_or(&DebugType),
848 "configuration": ResolvedConfig.clone(),
849 });
850
851 if let Err(error) = IPCProvider
852 .SendNotificationToSideCar(TargetSideCar.clone(), StartedMethod, json!([StartedSession]))
853 .await
854 {
855 dev_log!(
856 "exthost",
857 "warn: [DebugProvider] StartDebugging notification failed for '{}': {:?}",
858 SessionID,
859 error
860 );
861 }
862
863 let _ = self.ApplicationHandle.emit(
869 "sky://debug/sessionStart",
870 json!({
871 "sessionId": SessionID.clone(),
872 "type": DebugType.clone(),
873 "configuration": ResolvedConfig.clone(),
874 }),
875 );
876
877 dev_log!("exthost", "[DebugProvider] Debug session '{}' started (simulation).", SessionID);
878
879 Ok(SessionID)
880 }
881
882 async fn SendCommand(&self, SessionID:String, Command:String, Arguments:Value) -> Result<Value, CommonError> {
883 dev_log!(
884 "exthost",
885 "[DebugProvider] SendCommand for session '{}' (command: '{}', args: {:?})",
886 SessionID,
887 Command,
888 Arguments
889 );
890
891 let SessionEntry = self.ApplicationState.Feature.Debug.GetDebugSession(&SessionID);
896
897 let RequestSeq = Arguments.get("seq").and_then(Value::as_u64).unwrap_or(0);
908
909 let RequestArguments = Arguments.get("arguments").cloned().unwrap_or(Arguments.clone());
910
911 let DapRequest = json!({
912 "seq": RequestSeq,
913 "type": "request",
914 "command": Command,
915 "arguments": RequestArguments,
916 });
917
918 if let Some(Entry) = SessionEntry.as_ref() {
919 if let Some(Sender) = Entry.StdinSender.as_ref() {
920 let Body = serde_json::to_vec(&DapRequest).map_err(|Error| {
921 CommonError::IPCError {
922 Description:format!("Failed to serialize DAP request for session {}: {}", SessionID, Error),
923 }
924 })?;
925
926 let Header = format!("Content-Length: {}\r\n\r\n", Body.len());
927
928 let mut Frame = Vec::with_capacity(Header.len() + Body.len());
929
930 Frame.extend_from_slice(Header.as_bytes());
931
932 Frame.extend_from_slice(&Body);
933
934 Sender.send(Frame).map_err(|Error| {
935 CommonError::IPCError {
936 Description:format!("Adapter stdin channel for session {} closed: {}", SessionID, Error),
937 }
938 })?;
939
940 return Ok(json!({
947 "success": true,
948 "sessionId": SessionID,
949 "command": Command,
950 "transport": "stdio",
951 }));
952 }
953 }
954
955 let TargetSidecar = SessionEntry
964 .as_ref()
965 .map(|E| E.SideCarIdentifier.clone())
966 .unwrap_or_else(|| "cocoon-main".to_string());
967
968 let SendDapMethod = format!("{}$sendDAPRequest", ProxyTarget::ExtHostDebug.GetTargetPrefix());
969
970 let IPCProvider:Arc<dyn IPCProvider> = self.Require();
971
972 match IPCProvider
973 .SendRequestToSideCar(
974 TargetSidecar,
975 SendDapMethod,
976 json!([{ "sessionId": SessionID, "request": DapRequest }]),
977 15000,
978 )
979 .await
980 {
981 Ok(Response) => Ok(Response),
982
983 Err(Error) => {
984 dev_log!(
985 "exthost",
986 "warn: [DebugProvider] reverse-RPC SendCommand failed for session {}: {:?}",
987 SessionID,
988 Error
989 );
990
991 Err(Error)
992 },
993 }
994 }
995
996 async fn StopDebugging(&self, SessionID:String) -> Result<(), CommonError> {
997 dev_log!("exthost", "[DebugProvider] StopDebugging request for session '{}'", SessionID);
998
999 if let Some(Entry) = self.ApplicationState.Feature.Debug.GetDebugSession(&SessionID) {
1004 if let Some(Sender) = Entry.StdinSender.as_ref() {
1005 let DisconnectRequest = json!({
1006 "seq": 0,
1007 "type": "request",
1008 "command": "disconnect",
1009 "arguments": { "restart": false, "terminateDebuggee": true },
1010 });
1011
1012 if let Ok(Body) = serde_json::to_vec(&DisconnectRequest) {
1013 let Header = format!("Content-Length: {}\r\n\r\n", Body.len());
1014
1015 let mut Frame = Vec::with_capacity(Header.len() + Body.len());
1016
1017 Frame.extend_from_slice(Header.as_bytes());
1018
1019 Frame.extend_from_slice(&Body);
1020
1021 let _ = Sender.send(Frame);
1022 }
1023 }
1024 }
1025
1026 let _ = self.ApplicationState.Feature.Debug.UnregisterDebugSession(&SessionID);
1031
1032 let IPCProvider:Arc<dyn IPCProvider> = self.Require();
1033
1034 let TerminateMethod = format!("{}$onDidTerminateDebugSession", ProxyTarget::ExtHostDebug.GetTargetPrefix());
1035
1036 if let Err(error) = IPCProvider
1037 .SendNotificationToSideCar("cocoon-main".to_string(), TerminateMethod, json!([{ "id": SessionID.clone() }]))
1038 .await
1039 {
1040 dev_log!(
1041 "exthost",
1042 "warn: [DebugProvider] StopDebugging notification failed for '{}': {:?}",
1043 SessionID,
1044 error
1045 );
1046 }
1047
1048 let _ = self
1049 .ApplicationHandle
1050 .emit("sky://debug/sessionEnd", json!({ "sessionId": SessionID.clone() }));
1051
1052 Ok(())
1053 }
1054}