From 5279443ac5ed85e0cd88fc8fd5f7b8dbf3acef6f Mon Sep 17 00:00:00 2001 From: huanglininn <143576570+huanglininn@users.noreply.github.com> Date: Thu, 5 Nov 2026 09:55:02 +0800 Subject: [PATCH] fix(debugger): send Windows netcoredbg source paths with backslashes (#1409) Co-authored-by: moyining <724405566@qq.com> --- crates/ide-dap/src/session.rs | 16 ++++++++++------ crates/ide-dap/src/tests.rs | 17 +++++++++++++++++ 2 files changed, 27 insertions(+), 6 deletions(-) diff --git a/crates/ide-dap/src/session.rs b/crates/ide-dap/src/session.rs index 8ac55b92..f7bc69c3 100644 --- a/crates/ide-dap/src/session.rs +++ b/crates/ide-dap/src/session.rs @@ -111,8 +111,8 @@ impl Session { if let Some(path) = self.kotlin_mirror.lock().as_ref().and_then(|m| m.adapter_path(path)) { return path; } - if self.adapter == "java-debug" { - let native = java_source_path(&path.to_string_lossy()); + if self.adapter == "java-debug" || (cfg!(windows) && self.adapter == "netcoredbg") { + let native = windows_source_path(&path.to_string_lossy()); self.resolved_paths.lock().insert(native.clone(), path.to_path_buf()); return native; } @@ -133,7 +133,11 @@ impl Session { if let Some(real) = self.kotlin_mirror.lock().as_ref().and_then(|m| m.client_path(Path::new(path))) { return Some(real); } - let key = if self.adapter == "java-debug" { java_source_path(path) } else { PathBuf::from(path) }; + let key = if self.adapter == "java-debug" || (cfg!(windows) && self.adapter == "netcoredbg") { + windows_source_path(path) + } else { + PathBuf::from(path) + }; self.resolved_paths.lock().get(&key).cloned().or_else(|| { (self.adapter == "java-debug" && is_windows_source_path(path)) .then(|| PathBuf::from(path.replace('\\', "/"))) @@ -265,8 +269,8 @@ impl Session { } } -// java-debug treats a forward-slash drive path as a URI (scheme "C"). -// Keep DAP path mode, but use native Windows separators for Java source lookup. +// java-debug and Windows netcoredbg need native separators for source lookup. +// Keep DAP path mode, but normalize Windows drive and UNC paths. fn is_windows_source_path(path: &str) -> bool { let bytes = path.as_bytes(); (bytes.len() >= 3 && bytes[0].is_ascii_alphabetic() && bytes[1] == b':' && matches!(bytes[2], b'/' | b'\\')) @@ -274,7 +278,7 @@ fn is_windows_source_path(path: &str) -> bool { || path.starts_with(r"\\") } -fn java_source_path(path: &str) -> PathBuf { +fn windows_source_path(path: &str) -> PathBuf { if is_windows_source_path(path) { PathBuf::from(path.replace('/', r"\")) } else { PathBuf::from(path) } } diff --git a/crates/ide-dap/src/tests.rs b/crates/ide-dap/src/tests.rs index b7316019..1e5647b7 100644 --- a/crates/ide-dap/src/tests.rs +++ b/crates/ide-dap/src/tests.rs @@ -1141,6 +1141,23 @@ async fn kotlin_official_release_ten_no_delay_runs() { } } +#[cfg(windows)] +#[tokio::test] +async fn netcoredbg_windows_source_paths_round_trip() { + let client = "D:/work/LoginController.cs"; + let native = r"D:\work\LoginController.cs"; + let h = harness(Script { source_path: Some(native.into()), ..Script::default() }); + let path = PathBuf::from(client); + h.manager.set_breakpoints(path.clone(), vec![bp(25)]).await.unwrap(); + let mut cfg = config(); + cfg.adapter = "netcoredbg".into(); + let id = h.start(cfg).await.unwrap(); + assert_eq!(h.request("setBreakpoints")["arguments"]["source"]["path"], native); + let frames = h.manager.stack_trace(id, 1).await.unwrap(); + assert_eq!(frames[0].path.as_deref(), Some(path.as_path())); + h.manager.stop(id).await.unwrap(); +} + #[tokio::test] async fn java_windows_source_paths_for_launch_and_attach() { for request in ["launch", "attach"] {