diff --git a/crates/engine/src/file_cmds/tests.rs b/crates/engine/src/file_cmds/tests.rs index 3115d713b6..9132af44d9 100644 --- a/crates/engine/src/file_cmds/tests.rs +++ b/crates/engine/src/file_cmds/tests.rs @@ -1416,6 +1416,18 @@ fn reveal_in_finder_reveals_the_documents_saved_path() { assert_eq!(r["args"], json!(args)); } +#[cfg(windows)] +#[test] +fn reveal_in_finder_on_windows_passes_the_clicked_tabs_saved_path() { + let mut s = session(8, 8, 8); + s.active_mut().unwrap().path = Some("C:/项目 图片/海报 🖼.png".into()); + s.execute("file.new", json!({"width": 8, "height": 8, "name": "other"})).unwrap(); + + let r = s.execute("file.revealInFinder", json!({"document": 0, "dryRun": true})).unwrap(); + assert_eq!(r["program"], "explorer"); + assert_eq!(r["args"], json!(["/select,", r"C:\项目 图片\海报 🖼.png"])); +} + /// "Include All Subfolders" (AUTO-219-5, #3753): Batch and Image Processor take the images in /// subfolders too, each result keeping its subfolder under the output folder (so same-named files /// don't collide), and skip the output folder when it sits inside the input folder. diff --git a/crates/engine/src/layer_menu_cmds.rs b/crates/engine/src/layer_menu_cmds.rs index 9165990122..f86d18b646 100644 --- a/crates/engine/src/layer_menu_cmds.rs +++ b/crates/engine/src/layer_menu_cmds.rs @@ -421,7 +421,8 @@ fn reveal_command_on(os: &str, path: &str) -> Result<(&'static str, Vec) // Explorer splits `/select,` arguments at commas and reads quotes itself, so such a path // would select something else. "windows" if path.contains([',', '"']) => Err(other(format!("{path} can't be shown in Explorer: its path contains a comma or a quotation mark"))), - "windows" => Ok(("explorer", vec![format!("/select,{path}")])), + // Keep the switch outside the path's quotes: Explorer parses its own command line. + "windows" => Ok(("explorer", vec!["/select,".into(), path.replace('/', "\\")])), _ => { let parent = std::path::Path::new(path).parent().map_or_else(|| ".".to_string(), |p| p.to_string_lossy().into_owned()); Ok(("xdg-open", vec![parent])) diff --git a/crates/engine/src/layer_menu_cmds/tests.rs b/crates/engine/src/layer_menu_cmds/tests.rs index 4f5c6dc12f..b0cceb72a8 100644 --- a/crates/engine/src/layer_menu_cmds/tests.rs +++ b/crates/engine/src/layer_menu_cmds/tests.rs @@ -541,12 +541,26 @@ fn reveal_follows_linked_paths_like_the_link_and_passes_paths_as_paths() { s.execute("file.revealInFinder", json!({"dryRun": true})).unwrap(); assert_eq!(reveal_command_on("macos", "-a.png").unwrap(), ("open", vec!["-R".to_string(), "--".into(), "-a.png".into()])); - assert_eq!(reveal_command_on("windows", r"C:\pics\a.png").unwrap(), ("explorer", vec![r"/select,C:\pics\a.png".to_string()])); + assert_eq!(reveal_command_on("windows", r"C:\pics\a.png").unwrap(), ("explorer", vec!["/select,".to_string(), r"C:\pics\a.png".to_string()])); for path in [r"C:\pics\a,b.png", r#"C:\pics\a".png"#] { assert!(reveal_command_on("windows", path).is_err(), "{path}"); } } +/// Explorer parses `/select,` itself: it must remain outside the quoted file path. +#[test] +fn reveal_on_windows_keeps_the_select_switch_separate_from_the_path() { + for (path, expected) in [ + (r"C:\my pictures\a file.png", r"C:\my pictures\a file.png"), + ("C:/my pictures/a file.png", r"C:\my pictures\a file.png"), + (r"C:\my pictures/a file.png", r"C:\my pictures\a file.png"), + ("C:/项目 图片/海报 🖼.png", r"C:\项目 图片\海报 🖼.png"), + ("//server/share/my pictures/a file.png", r"\\server\share\my pictures\a file.png"), + ] { + assert_eq!(reveal_command_on("windows", path).unwrap(), ("explorer", vec!["/select,".to_string(), expected.to_string()]), "{path}"); + } +} + /// `xdg-open` launches a file it is given, so Reveal hands it the link's folder only when that is a /// folder: a link stored as `tool.jar/a.png` names a file, not a folder, as its parent. #[test]