Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 16 additions & 1 deletion app/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2131,7 +2131,22 @@ fn app_callbacks(is_integration_test: bool) -> warpui::platform::AppCallbacks {
})),
on_open_urls: Some(Box::new(move |urls, ctx| {
for url in &urls {
let parsed_url = Url::parse(url);
let parsed_url = Url::parse(url).or_else(|_| {
// On Windows, a bare path like "C:\folder" fails URL parsing
// because "C:" looks like a URI scheme. Convert to a file:// URL.
#[cfg(windows)]
{
let path = url.trim();
if path.len() >= 2
&& path.as_bytes()[1] == b':'
&& (path.as_bytes()[0].is_ascii_alphabetic())
{
let forward = path.replace('\\', "/");
return Url::parse(&format!("file:///{forward}"));
}
}
Err(url::ParseError::EmptyHost)
});
match parsed_url {
Ok(url) => uri::handle_incoming_uri(&url, ctx),
Err(e) => log::warn!("Unable to parse received url: {e}"),
Expand Down
18 changes: 16 additions & 2 deletions app/src/root_view.rs
Original file line number Diff line number Diff line change
Expand Up @@ -914,8 +914,22 @@ fn open_from_restored(arg: &OpenFromRestoredArg, ctx: &mut AppContext) {
}
}

fn path_if_directory(path: &Path) -> Option<&Path> {
path.is_dir().then_some(path)
fn path_if_directory(path: &Path) -> Option<PathBuf> {
if path.is_dir() {
return Some(path.to_path_buf());
}
// On Windows, paths from Explorer context menu may arrive without URL-encoding
// (e.g. `warp://action/new_tab?path=C:\Projects\my-app`). The url crate handles
// this correctly, but the resulting path can still fail `is_dir()` when the OS
// needs a normalized form (canonicalized, trailing-separator-stripped, etc.).
// Try canonicalizing as a fallback — this resolves symlinks, relative segments,
// and other minor path differences that `is_dir()` alone would reject.
if let Ok(canonical) = std::fs::canonicalize(path) {
if canonical.is_dir() {
return Some(canonical);
}
}
None
}

/// Opens a new window with the workspace configured according to `source`. Returns the
Expand Down
9 changes: 8 additions & 1 deletion app/src/uri/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -663,7 +663,14 @@ fn find_matching_config_name<'a>(
/// user's home directory.
fn parse_tab_path(url: &Url) -> Option<PathBuf> {
let raw = url.query_pairs().find(|(k, _)| k == "path")?.1;
Some(PathBuf::from(shellexpand::tilde(&raw).into_owned()))
let expanded = shellexpand::tilde(&raw).into_owned();
// Trim whitespace that may be appended by Windows shell variable expansion
// (e.g. `%1` or `%V` can carry trailing spaces/newlines in some configurations).
let trimmed = expanded.trim();
if trimmed.is_empty() {
return None;
}
Some(PathBuf::from(trimmed))
}

#[derive(Debug)]
Expand Down
36 changes: 36 additions & 0 deletions app/src/uri/uri_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -659,3 +659,39 @@ fn test_open_file_non_runnable_shebang_routes_to_editor() {
std::fs::set_permissions(&p, std::fs::Permissions::from_mode(0o644)).unwrap();
assert_eq!(classify_open_file_action(&p), OpenFileAction::Editor);
}

// -- parse_tab_path edge cases (GH #9844) -----------------------------------
//
// These tests cover the fix for Windows "Open Warp in new tab" context menu
// not navigating to the selected directory. The Windows shell passes raw
// paths via `%1` / `%V` without URL-encoding, so the query parameter may
// contain backslashes, colons, and trailing whitespace.

#[test]
fn test_parse_tab_path_windows_drive_letter() {
let url = Url::parse(r"warp://action/new_tab?path=C:\Projects\my-app").unwrap();
assert_eq!(
parse_tab_path(&url),
Some(PathBuf::from(r"C:\Projects\my-app"))
);
}

#[test]
fn test_parse_tab_path_windows_with_trailing_whitespace() {
let url = Url::parse("warp://action/new_tab?path=C%3A%5CProjects%20").unwrap();
// Trailing space should be trimmed
let result = parse_tab_path(&url);
assert!(result.is_some());
}

#[test]
fn test_parse_tab_path_empty_returns_none() {
let url = Url::parse("warp://action/new_tab?path=").unwrap();
assert_eq!(parse_tab_path(&url), None);
}

#[test]
fn test_parse_tab_path_whitespace_only_returns_none() {
let url = Url::parse("warp://action/new_tab?path=%20%20%20").unwrap();
assert_eq!(parse_tab_path(&url), None);
}