diff options
author | Nayeem Rahman <nayeemrmn99@gmail.com> | 2023-10-10 19:32:22 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-10-10 19:32:22 +0100 |
commit | 2215a3ea2e79dd579388872d6e12e6ee987e745b (patch) | |
tree | d1fae9630b69401c10ba6c9f75bf1b799fcb9fbf | |
parent | 7b80b27a96026cccb892815f19855b9aec79968c (diff) |
fix(lsp): normalize "deno:" urls statelessly (#20867)
-rw-r--r-- | cli/lsp/urls.rs | 100 |
1 files changed, 72 insertions, 28 deletions
diff --git a/cli/lsp/urls.rs b/cli/lsp/urls.rs index f5c170277..c3bd381d4 100644 --- a/cli/lsp/urls.rs +++ b/cli/lsp/urls.rs @@ -24,8 +24,6 @@ pub static INVALID_SPECIFIER: Lazy<ModuleSpecifier> = /// the component percent encoding set. /// /// See: <https://url.spec.whatwg.org/#component-percent-encode-set> -/// -// TODO(@kitsonk) - refactor when #9934 is landed. const COMPONENT: &percent_encoding::AsciiSet = &percent_encoding::CONTROLS .add(b' ') .add(b'"') @@ -47,6 +45,7 @@ const COMPONENT: &percent_encoding::AsciiSet = &percent_encoding::CONTROLS .add(b'^') .add(b'|') .add(b'$') + .add(b'%') .add(b'&') .add(b'+') .add(b','); @@ -60,6 +59,43 @@ fn hash_data_specifier(specifier: &ModuleSpecifier) -> String { crate::util::checksum::gen(&[file_name_str.as_bytes()]) } +fn to_deno_url(specifier: &Url) -> String { + let mut string = String::with_capacity(specifier.as_str().len() + 6); + string.push_str("deno:/"); + string.push_str(specifier.scheme()); + for p in specifier[Position::BeforeHost..].split('/') { + string.push('/'); + string.push_str( + &percent_encoding::utf8_percent_encode(p, COMPONENT).to_string(), + ); + } + string +} + +fn from_deno_url(url: &Url) -> Option<Url> { + if url.scheme() != "deno" { + return None; + } + let mut segments = url.path_segments()?; + let mut string = String::with_capacity(url.as_str().len()); + string.push_str(segments.next()?); + string.push_str("://"); + string.push_str( + &percent_encoding::percent_decode(segments.next()?.as_bytes()) + .decode_utf8() + .ok()?, + ); + for segment in segments { + string.push('/'); + string.push_str( + &percent_encoding::percent_decode(segment.as_bytes()) + .decode_utf8() + .ok()?, + ); + } + Url::parse(&string).ok() +} + /// This exists to make it a little bit harder to accidentally use a `Url` /// in the wrong place where a client url should be used. #[derive(Debug, Clone, Hash, PartialEq, Eq, Ord, PartialOrd)] @@ -171,16 +207,7 @@ impl LspUrlMap { extension ) } else { - let mut str = String::with_capacity(specifier.as_str().len() + 6); - str.push_str("deno:/"); - str.push_str(specifier.scheme()); - for p in specifier[Position::BeforeHost..].split('/') { - str.push('/'); - str.push_str( - &percent_encoding::utf8_percent_encode(p, COMPONENT).to_string(), - ); - } - str + to_deno_url(specifier) }; let url = LspClientUrl(Url::parse(&specifier_str)?); inner.put(specifier.clone(), url.clone()); @@ -210,23 +237,22 @@ impl LspUrlMap { } let mut inner = self.inner.lock(); if let Some(specifier) = inner.get_specifier(url).cloned() { - specifier - } else { - let specifier = if url.scheme() == "file" { - if let Ok(path) = url.to_file_path() { - match kind { - LspUrlKind::Folder => Url::from_directory_path(path).unwrap(), - LspUrlKind::File => Url::from_file_path(path).unwrap(), - } - } else { - url.clone() - } - } else { - url.clone() - }; - inner.put(specifier.clone(), LspClientUrl(url.clone())); - specifier + return specifier; + } + let mut specifier = None; + if url.scheme() == "file" { + if let Ok(path) = url.to_file_path() { + specifier = Some(match kind { + LspUrlKind::Folder => Url::from_directory_path(path).unwrap(), + LspUrlKind::File => Url::from_file_path(path).unwrap(), + }); + } + } else if let Some(s) = from_deno_url(url) { + specifier = Some(s); } + let specifier = specifier.unwrap_or_else(|| url.clone()); + inner.put(specifier.clone(), LspClientUrl(url.clone())); + specifier } } @@ -262,6 +288,24 @@ mod tests { } #[test] + fn test_lsp_url_reverse() { + let map = LspUrlMap::default(); + let fixture = + resolve_url("deno:/https/deno.land/x/pkg%401.0.0/mod.ts").unwrap(); + let actual_specifier = map.normalize_url(&fixture, LspUrlKind::File); + let expected_specifier = + Url::parse("https://deno.land/x/pkg@1.0.0/mod.ts").unwrap(); + assert_eq!(&actual_specifier, &expected_specifier); + + let actual_url = map + .normalize_specifier(&actual_specifier) + .unwrap() + .as_url() + .clone(); + assert_eq!(actual_url, fixture); + } + + #[test] fn test_lsp_url_map_complex_encoding() { // Test fix for #9741 - not properly encoding certain URLs let map = LspUrlMap::default(); |