Skip to content

Commit 2cda1fc

Browse files
committed
update
1 parent ceb97cb commit 2cda1fc

File tree

4 files changed

+88
-4
lines changed

4 files changed

+88
-4
lines changed

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
@@ -204,6 +204,7 @@ nonmax = "0.5.5"
204204
num-bigint = "0.4.6"
205205
num-traits = "0.2.19"
206206
papaya = "0.2.1"
207+
percent-encoding = "2.3.1"
207208
petgraph = { version = "0.8.2", default-features = false }
208209
phf = "0.11.3"
209210
phf_codegen = "0.11.3"

crates/oxc_diagnostics/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,4 @@ doctest = false
2121
[dependencies]
2222
cow-utils = { workspace = true }
2323
miette = { workspace = true }
24+
percent-encoding = { workspace = true }

crates/oxc_diagnostics/src/service.rs

Lines changed: 85 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
11
use std::{
2+
borrow::Cow,
23
io::{ErrorKind, Write},
34
path::{Path, PathBuf},
45
sync::{Arc, mpsc},
56
};
67

78
use cow_utils::CowUtils;
89
use miette::LabeledSpan;
10+
use percent_encoding::AsciiSet;
11+
#[cfg(not(windows))]
12+
use std::fs::canonicalize as strict_canonicalize;
913

1014
use crate::{
1115
Error, NamedSource, OxcDiagnostic, Severity,
@@ -142,13 +146,12 @@ impl DiagnosticService {
142146
std::env::var("TERMINAL_EMULATOR").is_ok_and(|x| x.eq("JetBrains-JediTerm"));
143147

144148
let path_ref = path.as_ref();
145-
let path_display = if is_jetbrains {
146-
format!("file://{}", path_ref.to_string_lossy())
147-
} else {
149+
let path_display = if is_jetbrains { from_file_path(path_ref) } else { None };
150+
let path_display = path_display.unwrap_or_else(|| {
148151
let relative_path = path_ref.strip_prefix(cwd).unwrap_or(path_ref).to_string_lossy();
149152
let normalized_path = relative_path.cow_replace('\\', "/");
150153
normalized_path.to_string()
151-
};
154+
});
152155

153156
let source = Arc::new(NamedSource::new(path_display, source_text.to_owned()));
154157
diagnostics
@@ -269,3 +272,81 @@ impl DiagnosticService {
269272
}
270273
}
271274
}
275+
276+
// The following from_file_path and strict_canonicalize implementations are from tower-lsp-community/tower-lsp-server
277+
// available under the MIT License or Apache 2.0 License.
278+
//
279+
// Copyright (c) 2023 Eyal Kalderon
280+
// https://github.com/tower-lsp-community/tower-lsp-server/blob/85506ddcbd108c514438e0b62e0eb858c812adcf/src/uri_ext.rs
281+
282+
const ASCII_SET: AsciiSet =
283+
// RFC3986 allows only alphanumeric characters, `-`, `.`, `_`, and `~` in the path.
284+
percent_encoding::NON_ALPHANUMERIC
285+
.remove(b'-')
286+
.remove(b'.')
287+
.remove(b'_')
288+
.remove(b'~')
289+
// we do not want path separators to be percent-encoded
290+
.remove(b'/');
291+
292+
fn from_file_path<A: AsRef<Path>>(path: A) -> Option<String> {
293+
let path = path.as_ref();
294+
295+
let fragment = if path.is_absolute() {
296+
Cow::Borrowed(path)
297+
} else {
298+
match strict_canonicalize(path) {
299+
Ok(path) => Cow::Owned(path),
300+
Err(_) => return None,
301+
}
302+
};
303+
304+
if cfg!(windows) {
305+
// we want to parse a triple-slash path for Windows paths
306+
// it's a shorthand for `file://localhost/C:/Windows` with the `localhost` omitted.
307+
// We encode the driver Letter `C:` as well. LSP Specification allows it.
308+
// https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#uri
309+
Some(format!(
310+
"file:///{}",
311+
percent_encoding::utf8_percent_encode(
312+
&fragment.to_string_lossy().cow_replace('\\', "/"),
313+
&ASCII_SET
314+
)
315+
))
316+
} else {
317+
Some(format!(
318+
"file://{}",
319+
percent_encoding::utf8_percent_encode(&fragment.to_string_lossy(), &ASCII_SET)
320+
))
321+
}
322+
}
323+
324+
/// On Windows, rewrites the wide path prefix `\\?\C:` to `C:`
325+
/// Source: https://stackoverflow.com/a/70970317
326+
#[inline]
327+
#[cfg(windows)]
328+
fn strict_canonicalize<P: AsRef<Path>>(path: P) -> std::io::Result<PathBuf> {
329+
use std::io;
330+
331+
fn impl_(path: PathBuf) -> std::io::Result<PathBuf> {
332+
let head = path.components().next().ok_or(io::Error::other("empty path"))?;
333+
let disk_;
334+
let head = if let std::path::Component::Prefix(prefix) = head {
335+
if let std::path::Prefix::VerbatimDisk(disk) = prefix.kind() {
336+
disk_ = format!("{}:", disk as char);
337+
Path::new(&disk_)
338+
.components()
339+
.next()
340+
.ok_or(io::Error::other("failed to parse disk component"))?
341+
} else {
342+
head
343+
}
344+
} else {
345+
head
346+
};
347+
Ok(std::iter::once(head).chain(path.components().skip(1)).collect())
348+
}
349+
350+
let canon = std::fs::canonicalize(path)?;
351+
impl_(canon)
352+
}

0 commit comments

Comments
 (0)