Skip to content
Closed
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
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,7 @@ nonmax = "0.5.5"
num-bigint = "0.4.6"
num-traits = "0.2.19"
papaya = "0.2.1"
percent-encoding = "2.3.1"
petgraph = { version = "0.8.1", default-features = false }
phf = "0.11.3"
phf_codegen = "0.11.3"
Expand Down
2 changes: 2 additions & 0 deletions crates/oxc_language_server/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,14 @@ oxc_diagnostics = { workspace = true }
oxc_linter = { workspace = true, features = ["language_server"] }

#
cow-utils = { workspace = true }
env_logger = { workspace = true, features = ["humantime"] }
futures = { workspace = true }
globset = { workspace = true }
ignore = { workspace = true, features = ["simd-accel"] }
log = { workspace = true }
papaya = { workspace = true }
percent-encoding = { workspace = true }
rustc-hash = { workspace = true }
serde = { workspace = true, features = ["derive"] }
serde_json = { workspace = true }
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
debugger;
4 changes: 2 additions & 2 deletions crates/oxc_language_server/src/linter/server_linter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -316,8 +316,8 @@ mod test {
Tester::new("fixtures/linter/vue", None).test_and_snapshot_single_file("debugger.vue");
Tester::new("fixtures/linter/svelte", None)
.test_and_snapshot_single_file("debugger.svelte");
// ToDo: fix Tester to work only with Uris and do not access the file system
// Tester::new("fixtures/linter/nextjs").test_and_snapshot_single_file("%5B%5B..rest%5D%5D/debugger.ts");
Tester::new("fixtures/linter/nextjs", None)
.test_and_snapshot_single_file("[[...rest]]/debugger.ts");
}

#[test]
Expand Down
2 changes: 2 additions & 0 deletions crates/oxc_language_server/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ mod linter;
mod options;
#[cfg(test)]
mod tester;
#[cfg(test)]
mod uri_ext;
mod worker;

type ConcurrentHashMap<K, V> = papaya::HashMap<K, V, FxBuildHasher>;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
---
source: crates/oxc_language_server/src/tester.rs
input_file: "crates/oxc_language_server/fixtures/linter/nextjs/[[...rest]]/debugger.ts"
---
code: "eslint(no-debugger)"
code_description.href: "https://oxc.rs/docs/guide/usage/linter/rules/eslint/no-debugger.html"
message: "`debugger` statement is not allowed\nhelp: Remove the debugger statement"
range: Range { start: Position { line: 0, character: 0 }, end: Position { line: 0, character: 9 } }
related_information[0].message: ""
related_information[0].location.uri: "file://<variable>/fixtures/linter/nextjs/%5B%5B...rest%5D%5D/debugger.ts"
related_information[0].location.range: Range { start: Position { line: 0, character: 0 }, end: Position { line: 0, character: 9 } }
severity: Some(Warning)
source: Some("oxc")
tags: None
fixed: Single(FixedContent { message: Some("Remove the debugger statement"), code: "", range: Range { start: Position { line: 0, character: 0 }, end: Position { line: 0, character: 9 } } })
4 changes: 2 additions & 2 deletions crates/oxc_language_server/src/tester.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@ use tower_lsp_server::{
lsp_types::{CodeDescription, NumberOrString, Uri},
};

use crate::{Options, worker::WorkspaceWorker};
use crate::{Options, uri_ext::path_to_uri, worker::WorkspaceWorker};

use super::linter::error_with_position::DiagnosticReport;

/// Given a file path relative to the crate root directory, return the URI of the file.
pub fn get_file_uri(relative_file_path: &str) -> Uri {
let absolute_file_path =
std::env::current_dir().expect("could not get current dir").join(relative_file_path);
Uri::from_file_path(absolute_file_path).expect("failed to convert file path to URL")
path_to_uri(&absolute_file_path)
}

fn get_snapshot_from_report(report: &DiagnosticReport) -> String {
Expand Down
96 changes: 96 additions & 0 deletions crates/oxc_language_server/src/uri_ext.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
use std::{path::Path, str::FromStr};

use cow_utils::CowUtils;
use percent_encoding::AsciiSet;
use tower_lsp_server::lsp_types::Uri;

pub fn path_to_uri(path: &Path) -> Uri {
let path = if cfg!(target_os = "windows") {
// On Windows, we need to replace backslashes with forward slashes.
// Tripleslash is a shorthand for `file://localhost/C:/Windows` with the `localhost` omitted.
// We encode the driver Letter `C:` as well. LSP Specification allows it.
// https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#uri
format!(
"file:///{}",
percent_encoding::utf8_percent_encode(
&path.to_string_lossy().cow_replace('\\', "/"),
&ASCII_SET
)
)
} else {
// For Unix-like systems, just convert to a file URI directly
format!(
"file://{}",
percent_encoding::utf8_percent_encode(&path.to_string_lossy(), &ASCII_SET)
)
};
Uri::from_str(&path).expect("Failed to create URI from path")
}

const ASCII_SET: AsciiSet =
// RFC3986 allows only alphanumeric characters, `-`, `.`, `_`, and `~` in the path.
percent_encoding::NON_ALPHANUMERIC
.remove(b'-')
.remove(b'.')
.remove(b'_')
.remove(b'~')
// we do not want path separators to be percent-encoded
.remove(b'/');

#[cfg(test)]
mod test {
use std::path::PathBuf;

use crate::uri_ext::path_to_uri;

const EXPECTED_SCHEMA: &str = if cfg!(target_os = "windows") { "file:///" } else { "file://" };

fn with_schema(path: &str) -> String {
format!("{EXPECTED_SCHEMA}{path}")
}

#[test]
fn test_path_to_uri() {
let path = PathBuf::from("/some/path/to/file.txt");
let uri = path_to_uri(&path);
assert_eq!(uri.to_string(), with_schema("/some/path/to/file.txt"));
}

#[test]
fn test_path_to_uri_with_spaces() {
let path = PathBuf::from("/some/path/to/file with spaces.txt");
let uri = path_to_uri(&path);
assert_eq!(uri.to_string(), with_schema("/some/path/to/file%20with%20spaces.txt"));
}

#[test]
fn test_path_to_uri_with_special_characters() {
let path = PathBuf::from("/some/path/[[...rest]]/file.txt");
let uri = path_to_uri(&path);
assert_eq!(uri.to_string(), with_schema("/some/path/%5B%5B...rest%5D%5D/file.txt"));
}

#[test]
fn test_path_to_uri_non_ascii() {
let path = PathBuf::from("/some/path/to/файл.txt");
let uri = path_to_uri(&path);
assert_eq!(uri.to_string(), with_schema("/some/path/to/%D1%84%D0%B0%D0%B9%D0%BB.txt"));
}

#[test]
fn test_path_to_uri_with_unicode() {
let path = PathBuf::from("/some/path/to/文件.txt");
let uri = path_to_uri(&path);
assert_eq!(uri.to_string(), with_schema("/some/path/to/%E6%96%87%E4%BB%B6.txt"));
}

#[cfg(all(test, target_os = "windows"))]
#[test]
fn test_path_to_uri_windows() {
let path = PathBuf::from("C:\\some\\path\\to\\file.txt");
let uri = path_to_uri(&path);
// yes we encode `:` too, LSP allows it
// https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#uri
assert_eq!(uri.to_string(), with_schema("C%3A/some/path/to/file.txt"));
}
}
Loading