From a3fb14fa8632abfca2e18877abbae5e535018100 Mon Sep 17 00:00:00 2001 From: l0ng-ai <24760907+l0ng-ai@users.noreply.github.com> Date: Sun, 1 Nov 2026 17:10:34 +0800 Subject: [PATCH] fix(java): align debug JVM encoding with Run --- crates/ide-core/src/output_encoding.rs | 69 ++++++++++++++++++++++---- crates/ide-host/src/ops/debug.rs | 21 ++++++-- 2 files changed, 77 insertions(+), 13 deletions(-) diff --git a/crates/ide-core/src/output_encoding.rs b/crates/ide-core/src/output_encoding.rs index f5e85f3d..de53b540 100644 --- a/crates/ide-core/src/output_encoding.rs +++ b/crates/ide-core/src/output_encoding.rs @@ -215,13 +215,21 @@ fn fix_kind( } /// `vmArgs` of a launch by java-debug, which decodes the JVM's output as `encoding` -/// (its launch attribute, UTF-8 when unset) and sets `file.encoding` itself. -pub fn java_debug_vm_args(vm_args: &str, encoding: Option<&str>, windows: bool) -> Option { +/// (its launch attribute, UTF-8 when unset). java-debug sets `file.encoding` only +/// when that attribute is nonblank; use the same defaults and user overrides as [`fix`]. +pub fn java_debug_vm_args( + vm_args: &str, + vars: &BTreeMap, + inherited: impl Fn(&str) -> Option, + encoding: Option<&str>, + windows: bool, +) -> Option { let encoding = encoding.map(str::trim).filter(|e| !e.is_empty()).unwrap_or("UTF-8"); - if (!windows && is_utf8(encoding)) || sets(vm_args, STD_PROPERTIES) { + let added = fix_kind(Kind::Java, Some(&[vm_args.to_owned()]), vars, inherited, encoding, windows).java_args; + if added.is_empty() { return None; } - let added = options(STD_PROPERTIES, encoding).join(" "); + let added = added.join(" "); Some(match vm_args.trim() { "" => added, own => format!("{own} {added}"), @@ -446,12 +454,53 @@ mod tests { #[test] fn java_debug_launches_write_what_java_debug_decodes() { - assert_eq!(java_debug_vm_args("", None, true).as_deref(), Some(TOOL_UTF8)); - assert_eq!(java_debug_vm_args("-Xmx1g", Some(""), true), Some(format!("-Xmx1g {TOOL_UTF8}"))); - assert_eq!(java_debug_vm_args("", Some("GBK"), true).unwrap().split(' ').next(), Some("-Dstdout.encoding=GBK")); - assert_eq!(java_debug_vm_args("-Dstdout.encoding=GBK", None, true), None); - assert_eq!(java_debug_vm_args("-Xmx1g", None, false), None); - assert!(java_debug_vm_args("", Some("GBK"), false).is_some()); + let debug = + |args: &str, encoding, windows| java_debug_vm_args(args, &BTreeMap::new(), |_| None, encoding, windows); + for encoding in [None, Some(""), Some(" "), Some("UTF-8"), Some("utf8")] { + let streams = encoding.filter(|e| !e.trim().is_empty()).unwrap_or("UTF-8"); + let utf8 = format!("{} -Dfile.encoding=UTF-8", options(STD_PROPERTIES, streams).join(" ")); + assert_eq!(debug("", encoding, true), Some(utf8.clone())); + assert_eq!(debug("-Xmx1g", encoding, true), Some(format!("-Xmx1g {utf8}"))); + assert_eq!(debug("-Xmx1g", encoding, false), None); + } + for windows in [true, false] { + assert_eq!(debug("", Some("GBK"), windows), Some(options(STD_PROPERTIES, "GBK").join(" "))); + } + } + + #[test] + fn java_debug_preserves_user_options_independently() { + let debug = |args: &str| java_debug_vm_args(args, &BTreeMap::new(), |_| None, None, true); + assert_eq!(debug("-Dfile.encoding=GBK"), Some(format!("-Dfile.encoding=GBK {TOOL_UTF8}"))); + for property in STD_PROPERTIES { + let args = format!("-D{property}=GBK"); + assert_eq!(debug(&args), Some(format!("{args} -Dfile.encoding=UTF-8"))); + assert_eq!(debug(&format!("{args} -Dfile.encoding=GBK")), None); + } + for key in ["JDK_JAVA_OPTIONS", TOOL_OPTIONS] { + for (own, expected) in [ + ("-Dfile.encoding=GBK", Some(TOOL_UTF8.to_owned())), + ("-Dstdout.encoding=GBK", Some("-Dfile.encoding=UTF-8".to_owned())), + ("-Dstdout.encoding=GBK -Dfile.encoding=GBK", None), + ] { + assert_eq!(java_debug_vm_args("", &vars(&[(key, own)]), |_| None, None, true), expected); + assert_eq!( + java_debug_vm_args("", &BTreeMap::new(), |k| (k == key).then(|| own.to_owned()), None, true), + expected + ); + } + // A launch environment value overrides the inherited one, as in Run. + assert_eq!( + java_debug_vm_args( + "", + &vars(&[(key, "")]), + |k| (k == key).then(|| "-Dfile.encoding=GBK".into()), + None, + true + ), + Some(format!("{TOOL_UTF8} -Dfile.encoding=UTF-8")) + ); + } } fn filtered(chunks: &[&str]) -> String { diff --git a/crates/ide-host/src/ops/debug.rs b/crates/ide-host/src/ops/debug.rs index 492ce842..9dd91de7 100644 --- a/crates/ide-host/src/ops/debug.rs +++ b/crates/ide-host/src/ops/debug.rs @@ -96,7 +96,13 @@ fn match_java_debug_encoding(mut config: LaunchConfig, windows: bool) -> LaunchC }; let vm_args = extra.get("vmArgs").and_then(|v| v.as_str()).unwrap_or_default(); let encoding = extra.get("encoding").and_then(|v| v.as_str()); - if let Some(vm_args) = ide_core::output_encoding::java_debug_vm_args(vm_args, encoding, windows) { + if let Some(vm_args) = ide_core::output_encoding::java_debug_vm_args( + vm_args, + &config.env, + |key| std::env::var(key).ok(), + encoding, + windows, + ) { extra.insert("vmArgs".into(), vm_args.into()); config.extra_json = Some(serde_json::Value::Object(extra).to_string()); } @@ -452,7 +458,7 @@ mod tests { program: None, args: vec![], cwd: None, - env: Default::default(), + env: [("JDK_JAVA_OPTIONS".into(), "".into()), ("JAVA_TOOL_OPTIONS".into(), "".into())].into(), extra_json: Some(extra.to_string()), } } @@ -468,7 +474,7 @@ mod tests { let fixed = match_java_debug_encoding(launch("java-debug", "launch", extra.clone()), true); assert_eq!( vm_args(&fixed), - "-Xmx1g -Dstdout.encoding=UTF-8 -Dstderr.encoding=UTF-8 -Dsun.stdout.encoding=UTF-8 -Dsun.stderr.encoding=UTF-8" + "-Xmx1g -Dstdout.encoding=UTF-8 -Dstderr.encoding=UTF-8 -Dsun.stdout.encoding=UTF-8 -Dsun.stderr.encoding=UTF-8 -Dfile.encoding=UTF-8" ); assert_eq!( serde_json::from_str::(fixed.extra_json.as_deref().unwrap()).unwrap()["mainClass"], @@ -481,4 +487,13 @@ mod tests { assert_eq!(vm_args(&match_java_debug_encoding(launch(adapter, request, extra.clone()), windows)), "-Xmx1g"); } } + + #[test] + fn java_debug_launches_respect_environment_encoding() { + for key in ["JDK_JAVA_OPTIONS", "JAVA_TOOL_OPTIONS"] { + let mut config = launch("java-debug", "launch", serde_json::json!({"vmArgs": "-Dstdout.encoding=GBK"})); + config.env.insert(key.into(), "-Dfile.encoding=GBK".into()); + assert_eq!(vm_args(&match_java_debug_encoding(config, true)), "-Dstdout.encoding=GBK"); + } + } }