Skip to content

Commit fc50e77

Browse files
authored
Add percent-encoding to file URI conversion (#231)
Replace inline URI formatting logic with a proper utility function that handles percent-encoding of special characters in file paths. This ensures file:// URIs are correctly formatted per RFC 3986, particularly for paths with spaces and other special characters. Address #230
1 parent 25b09a5 commit fc50e77

5 files changed

Lines changed: 76 additions & 10 deletions

File tree

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ crate-type = ["cdylib"]
1717

1818
[dependencies]
1919
hex = "0.4.3"
20+
percent-encoding = "2.3.2"
2021
regex = "1.12.2"
2122
serde = { version = "1.0.228", features = ["derive"] }
2223
serde_json = "1.0.145"

README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ This extension adds support for the Java language to [Zed](https://zed.dev). It
66

77
Install the extension via Zeds extension manager. It should work out of the box for most people. However, there are some things to know:
88

9-
- It is generally recommended to open projects with the Zed-project root at the Java project root folder (where you would commonly have your `pom.xml` or `build.gradle` file).
9+
- It is generally recommended to open projects with the Zed-project root at the Java project root folder (where you would commonly have your `pom.xml` or `build.gradle` file). The extension will automatically detect Maven and Gradle projects in subdirectories, but opening at the project root provides the best experience.
1010

1111
- By default the extension will download and run the latest official version of JDTLS for you, but this requires Java version 21 to be available on your system via either the `$JAVA_HOME` environment variable or as a `java(.exe)` executable on your `$PATH`. If your project requires a lower Java version in the environment, you can specify a different JDK to use for running JDTLS via the `java_home` configuration option.
1212

@@ -115,9 +115,9 @@ JDTLS provides many configuration options that can be passed via the `initialize
115115
"jdtls": {
116116
"initialization_options": {
117117
"bundles": [],
118-
"workspaceFolders": [
119-
"file:///home/snjeza/Project"
120-
],
118+
// The extension automatically sets this to the worktree root.
119+
// Override only if your Java project root differs from the opened folder:
120+
// "workspaceFolders": ["file:///path/to/your/java/project"],
121121
"settings": {
122122
"java": {
123123
"configuration": {

src/java.rs

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -388,12 +388,7 @@ impl Extension for Java {
388388
// Inject workspaceFolders default if not already set by the user
389389
let options_obj = options.as_object_mut().unwrap();
390390
if !options_obj.contains_key("workspaceFolders") {
391-
let root = worktree.root_path();
392-
let uri = if root.starts_with('/') {
393-
format!("file://{root}")
394-
} else {
395-
format!("file:///{}", root.replace('\\', "/"))
396-
};
391+
let uri = util::path_to_file_uri(&worktree.root_path());
397392
options_obj.insert("workspaceFolders".to_string(), json!([uri]));
398393
}
399394

src/util.rs

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use percent_encoding::utf8_percent_encode;
12
use regex::Regex;
23
use serde::{Deserialize, Serialize, Serializer};
34
use std::{
@@ -316,6 +317,42 @@ pub fn path_to_string<P: AsRef<Path>>(path: P) -> zed::Result<String> {
316317
.map_err(|_| PATH_TO_STR_ERROR.to_string())
317318
}
318319

320+
/// Characters to percent-encode in the path component of a file:// URI.
321+
/// Encodes everything except characters that are valid unencoded in URI paths per RFC 3986.
322+
const PATH_ENCODE_SET: percent_encoding::AsciiSet = percent_encoding::NON_ALPHANUMERIC
323+
.remove(b'/')
324+
.remove(b':')
325+
.remove(b'-')
326+
.remove(b'.')
327+
.remove(b'_')
328+
.remove(b'~')
329+
.remove(b'@');
330+
331+
/// Converts a filesystem path to a `file://` URI with proper percent-encoding.
332+
///
333+
/// Handles both Unix (`/home/user/project`) and Windows (`C:\Users\user\project`) paths.
334+
///
335+
/// # Arguments
336+
///
337+
/// * `path` - The filesystem path to convert.
338+
///
339+
/// # Returns
340+
///
341+
/// A properly encoded `file://` URI string.
342+
pub fn path_to_file_uri(path: &str) -> String {
343+
let mut uri = String::with_capacity(path.len() + 8);
344+
uri.push_str("file://");
345+
if path.starts_with('/') {
346+
uri.extend(utf8_percent_encode(path, &PATH_ENCODE_SET));
347+
} else {
348+
for chunk in path.split('\\') {
349+
uri.push('/');
350+
uri.extend(utf8_percent_encode(chunk, &PATH_ENCODE_SET));
351+
}
352+
}
353+
uri
354+
}
355+
319356
/// Remove all files or directories that aren't equal to [`filename`].
320357
///
321358
/// This function scans the directory given by [`prefix`] and removes any
@@ -498,4 +535,36 @@ mod tests {
498535
let serialized = serde_json::to_value(&wrapper).unwrap();
499536
assert_eq!(serialized["args"], "");
500537
}
538+
539+
#[test]
540+
fn test_file_uri_unix_path() {
541+
assert_eq!(
542+
path_to_file_uri("/home/user/project"),
543+
"file:///home/user/project"
544+
);
545+
}
546+
547+
#[test]
548+
fn test_file_uri_unix_path_with_spaces() {
549+
assert_eq!(
550+
path_to_file_uri("/my/path with/spaces"),
551+
"file:///my/path%20with/spaces"
552+
);
553+
}
554+
555+
#[test]
556+
fn test_file_uri_windows_path() {
557+
assert_eq!(
558+
path_to_file_uri(r"C:\Users\user\project"),
559+
"file:///C:/Users/user/project"
560+
);
561+
}
562+
563+
#[test]
564+
fn test_file_uri_windows_path_with_spaces() {
565+
assert_eq!(
566+
path_to_file_uri(r"C:\Users\My User\project"),
567+
"file:///C:/Users/My%20User/project"
568+
);
569+
}
501570
}

0 commit comments

Comments
 (0)