1
- use std:: { path:: { Path , PathBuf } , str:: FromStr } ;
1
+ use std:: {
2
+ path:: { Path , PathBuf } ,
3
+ str:: FromStr ,
4
+ } ;
2
5
6
+ use cow_utils:: CowUtils ;
3
7
use percent_encoding:: AsciiSet ;
4
8
use tower_lsp_server:: lsp_types:: Uri ;
5
9
6
10
fn path_to_uri ( path : & PathBuf ) -> Uri {
7
11
let path_str = normalize_path_with_utf8_percent_encode ( path) ;
8
- Uri :: from_str ( & format ! ( "file://{}" , path_str. to_string_lossy( ) ) ) . expect ( "Failed to create URI from path" )
12
+ let path = if cfg ! ( target_os = "windows" ) {
13
+ // On Windows, we need to convert the path to a URI format
14
+ // that includes replaces ckslashes with forward slashes.
15
+ // Tripleslash is a shorthand for `file://localhost/C:/Windows` with the `localhost` omitted
16
+ format ! ( "file:///{}" , path_str. to_string_lossy( ) . cow_replace( '\\' , "/" ) )
17
+ } else {
18
+ // For Unix-like systems, just convert to a file URI directly
19
+ format ! ( "file://{}" , path_str. to_string_lossy( ) )
20
+ } ;
21
+ Uri :: from_str ( & path) . expect ( "Failed to create URI from path" )
9
22
}
10
23
11
- const ASCII_SET : AsciiSet = percent_encoding:: NON_ALPHANUMERIC . remove ( b'.' ) ;
24
+ const ASCII_SET : AsciiSet =
25
+ percent_encoding:: NON_ALPHANUMERIC . remove ( b'-' ) . remove ( b'.' ) . remove ( b'_' ) . remove ( b'~' ) ;
12
26
13
27
/// Normalize a path by removing `.` and resolving `..` components,
14
28
/// without touching the filesystem.
@@ -20,15 +34,24 @@ pub fn normalize_path_with_utf8_percent_encode<P: AsRef<Path>>(path: P) -> PathB
20
34
match component {
21
35
std:: path:: Component :: Prefix ( _) => {
22
36
// Keep the prefix (e.g., drive letter on Windows)
23
- result. push ( component. as_os_str ( ) ) ;
37
+ result. push (
38
+ percent_encoding:: utf8_percent_encode (
39
+ component. as_os_str ( ) . to_str ( ) . unwrap ( ) ,
40
+ & ASCII_SET ,
41
+ )
42
+ . to_string ( ) ,
43
+ ) ;
24
44
}
25
45
std:: path:: Component :: RootDir => {
26
46
// Keep the root directory
27
47
result. push ( component. as_os_str ( ) ) ;
28
48
}
29
49
std:: path:: Component :: Normal ( part) => {
30
50
// Normal components are added to the path
31
- result. push ( percent_encoding:: utf8_percent_encode ( & part. to_str ( ) . unwrap ( ) , & ASCII_SET ) . to_string ( ) ) ;
51
+ result. push (
52
+ percent_encoding:: utf8_percent_encode ( part. to_str ( ) . unwrap ( ) , & ASCII_SET )
53
+ . to_string ( ) ,
54
+ ) ;
32
55
}
33
56
_ => { }
34
57
}
@@ -42,26 +65,45 @@ mod test {
42
65
43
66
use crate :: uri_ext:: path_to_uri;
44
67
68
+ const EXPECTED_SCHEMA : & str = if cfg ! ( target_os = "windows" ) { "file:///" } else { "file://" } ;
69
+
70
+ fn with_schema ( path : & str ) -> String {
71
+ format ! ( "{EXPECTED_SCHEMA}{path}" )
72
+ }
45
73
46
74
#[ test]
47
75
fn test_path_to_uri ( ) {
48
76
let path = PathBuf :: from ( "/some/path/to/file.txt" ) ;
49
77
let uri = path_to_uri ( & path) ;
50
- assert_eq ! ( uri. to_string( ) , "file:/// some/path/to/file.txt") ;
78
+ assert_eq ! ( uri. to_string( ) , with_schema ( "/ some/path/to/file.txt") ) ;
51
79
}
52
80
53
81
#[ test]
54
82
fn test_path_to_uri_with_spaces ( ) {
55
83
let path = PathBuf :: from ( "/some/path/to/file with spaces.txt" ) ;
56
84
let uri = path_to_uri ( & path) ;
57
- assert_eq ! ( uri. to_string( ) , "file:/// some/path/to/file%20with%20spaces.txt") ;
85
+ assert_eq ! ( uri. to_string( ) , with_schema ( "/ some/path/to/file%20with%20spaces.txt") ) ;
58
86
}
59
87
60
88
#[ test]
61
-
62
89
fn test_path_to_uri_with_special_characters ( ) {
63
90
let path = PathBuf :: from ( "/some/path/[[...rest]]/file.txt" ) ;
64
91
let uri = path_to_uri ( & path) ;
65
- assert_eq ! ( uri. to_string( ) , "file:///some/path/%5B%5B...rest%5D%5D/file.txt" ) ;
92
+ assert_eq ! ( uri. to_string( ) , with_schema( "/some/path/%5B%5B...rest%5D%5D/file.txt" ) ) ;
93
+ }
94
+
95
+ #[ test]
96
+ fn test_path_to_uri_non_ascii ( ) {
97
+ let path = PathBuf :: from ( "/some/path/to/файл.txt" ) ;
98
+ let uri = path_to_uri ( & path) ;
99
+ assert_eq ! ( uri. to_string( ) , with_schema( "/some/path/to/%D1%84%D0%B0%D0%B9%D0%BB.txt" ) ) ;
100
+ }
101
+
102
+ #[ cfg( all( test, target_os = "windows" ) ) ]
103
+ #[ test]
104
+ fn test_path_to_uri_windows ( ) {
105
+ let path = PathBuf :: from ( "C:\\ some\\ path\\ to\\ file.txt" ) ;
106
+ let uri = path_to_uri ( & path) ;
107
+ assert_eq ! ( uri. to_string( ) , with_schema( "c%3A/some/path/to/file.txt" ) ) ;
66
108
}
67
109
}
0 commit comments