diff options
-rw-r--r-- | cli/fs_util.rs | 112 | ||||
-rw-r--r-- | cli/lsp/completions.rs | 176 | ||||
-rw-r--r-- | cli/lsp/tsc.rs | 14 |
3 files changed, 96 insertions, 206 deletions
diff --git a/cli/fs_util.rs b/cli/fs_util.rs index 552f4c939..a27586da2 100644 --- a/cli/fs_util.rs +++ b/cli/fs_util.rs @@ -1053,34 +1053,92 @@ mod tests { #[test] fn test_relative_specifier() { - run_test("file:///from", "file:///to", Some("./to")); - run_test("file:///from", "file:///from/other", Some("./from/other")); - run_test("file:///from", "file:///from/other/", Some("./from/other/")); - run_test("file:///from", "file:///other/from", Some("./other/from")); - run_test("file:///from/", "file:///other/from", Some("../other/from")); - run_test("file:///from", "file:///other/from/", Some("./other/from/")); - run_test( - "file:///from", - "file:///to/other.txt", - Some("./to/other.txt"), - ); - run_test( - "file:///from/test", - "file:///to/other.txt", - Some("../to/other.txt"), - ); - run_test( - "file:///from/other.txt", - "file:///to/other.txt", - Some("../to/other.txt"), - ); - - fn run_test(from: &str, to: &str, expected: Option<&str>) { - let result = relative_specifier( - &ModuleSpecifier::parse(from).unwrap(), - &ModuleSpecifier::parse(to).unwrap(), + let fixtures: Vec<(&str, &str, Option<&str>)> = vec![ + ("file:///from", "file:///to", Some("./to")), + ("file:///from", "file:///from/other", Some("./from/other")), + ("file:///from", "file:///from/other/", Some("./from/other/")), + ("file:///from", "file:///other/from", Some("./other/from")), + ("file:///from/", "file:///other/from", Some("../other/from")), + ("file:///from", "file:///other/from/", Some("./other/from/")), + ( + "file:///from", + "file:///to/other.txt", + Some("./to/other.txt"), + ), + ( + "file:///from/test", + "file:///to/other.txt", + Some("../to/other.txt"), + ), + ( + "file:///from/other.txt", + "file:///to/other.txt", + Some("../to/other.txt"), + ), + ( + "https://deno.land/x/a/b/d.ts", + "https://deno.land/x/a/b/c.ts", + Some("./c.ts"), + ), + ( + "https://deno.land/x/a/b/d.ts", + "https://deno.land/x/a/c.ts", + Some("../c.ts"), + ), + ( + "https://deno.land/x/a/b/d.ts", + "https://deno.land/x/a/b/c/d.ts", + Some("./c/d.ts"), + ), + ( + "https://deno.land/x/a/b/c/", + "https://deno.land/x/a/b/c/d.ts", + Some("./d.ts"), + ), + ( + "https://deno.land/x/a/b/c/", + "https://deno.land/x/a/b/c/d/e.ts", + Some("./d/e.ts"), + ), + ( + "https://deno.land/x/a/b/c/f.ts", + "https://deno.land/x/a/b/c/d/e.ts", + Some("./d/e.ts"), + ), + ( + "https://deno.land/x/a/b/d.ts", + "https://deno.land/x/a/c.ts?foo=bar", + Some("../c.ts?foo=bar"), + ), + ( + "https://deno.land/x/a/b/d.ts?foo=bar", + "https://deno.land/x/a/b/c.ts", + Some("./c.ts"), + ), + ("file:///a/b/d.ts", "file:///a/b/c.ts", Some("./c.ts")), + ("https://deno.land/x/a/b/c.ts", "file:///a/b/c.ts", None), + ( + "https://deno.land/", + "https://deno.land/x/a/b/c.ts", + Some("./x/a/b/c.ts"), + ), + ( + "https://deno.land/x/d/e/f.ts", + "https://deno.land/x/a/b/c.ts", + Some("../../a/b/c.ts"), + ), + ]; + for (from_str, to_str, expected) in fixtures { + let from = ModuleSpecifier::parse(from_str).unwrap(); + let to = ModuleSpecifier::parse(to_str).unwrap(); + let actual = relative_specifier(&from, &to); + assert_eq!( + actual.as_deref(), + expected, + "from: \"{}\" to: \"{}\"", + from_str, + to_str ); - assert_eq!(result.as_deref(), expected); } } diff --git a/cli/lsp/completions.rs b/cli/lsp/completions.rs index b223c71a2..67978fbc9 100644 --- a/cli/lsp/completions.rs +++ b/cli/lsp/completions.rs @@ -8,6 +8,7 @@ use super::registries::ModuleRegistry; use super::tsc; use crate::fs_util::is_supported_ext; +use crate::fs_util::relative_specifier; use crate::fs_util::specifier_to_file_path; use deno_ast::LineAndColumnIndex; @@ -378,7 +379,7 @@ fn get_local_completions( if &entry_specifier == base { return None; } - let full_text = relative_specifier(&entry_specifier, base); + let full_text = relative_specifier(base, &entry_specifier)?; // this weeds out situations where we are browsing in the parent, but // we want to filter out non-matches when the completion is manually // invoked by the user, but still allows for things like `../src/../` @@ -443,7 +444,7 @@ fn get_relative_specifiers( .iter() .filter_map(|s| { if s != base { - Some(relative_specifier(s, base)) + Some(relative_specifier(base, s).unwrap_or_else(|| s.to_string())) } else { None } @@ -501,103 +502,6 @@ fn get_workspace_completions( .collect() } -/// Converts a specifier into a relative specifier to the provided base -/// specifier as a string. If a relative path cannot be found, then the -/// specifier is simply returned as a string. -/// -/// ``` -/// use deno_core::resolve_url; -/// -/// let specifier = resolve_url("file:///a/b.ts").unwrap(); -/// let base = resolve_url("file:///a/c/d.ts").unwrap(); -/// assert_eq!(relative_specifier(&specifier, &base), "../b.ts"); -/// ``` -/// -pub fn relative_specifier( - specifier: &ModuleSpecifier, - base: &ModuleSpecifier, -) -> String { - if specifier.cannot_be_a_base() - || base.cannot_be_a_base() - || specifier.scheme() != base.scheme() - || specifier.host() != base.host() - || specifier.port_or_known_default() != base.port_or_known_default() - { - if specifier.scheme() == "file" { - specifier_to_file_path(specifier) - .unwrap() - .to_string_lossy() - .into() - } else { - specifier.as_str().into() - } - } else if let (Some(iter_a), Some(iter_b)) = - (specifier.path_segments(), base.path_segments()) - { - let mut vec_a: Vec<&str> = iter_a.collect(); - let mut vec_b: Vec<&str> = iter_b.collect(); - let last_a = if !specifier.path().ends_with('/') && !vec_a.is_empty() { - vec_a.pop().unwrap() - } else { - "" - }; - let is_dir_b = base.path().ends_with('/'); - if !is_dir_b && !vec_b.is_empty() { - vec_b.pop(); - } - if !vec_a.is_empty() && !vec_b.is_empty() && base.path() != "/" { - let mut parts: Vec<&str> = Vec::new(); - let mut segments_a = vec_a.into_iter(); - let mut segments_b = vec_b.into_iter(); - loop { - match (segments_a.next(), segments_b.next()) { - (None, None) => break, - (Some(a), None) => { - if parts.is_empty() { - parts.push(CURRENT_PATH); - } - parts.push(a); - parts.extend(segments_a.by_ref()); - break; - } - (None, _) if is_dir_b => parts.push(CURRENT_PATH), - (None, _) => parts.push(PARENT_PATH), - (Some(a), Some(b)) if parts.is_empty() && a == b => (), - (Some(a), Some(b)) if b == CURRENT_PATH => parts.push(a), - (Some(_), Some(b)) if b == PARENT_PATH => { - return specifier[Position::BeforePath..].to_string() - } - (Some(a), Some(_)) => { - if parts.is_empty() && is_dir_b { - parts.push(CURRENT_PATH); - } else { - parts.push(PARENT_PATH); - } - // actually the clippy suggestions here are less readable for once - #[allow(clippy::same_item_push)] - for _ in segments_b { - parts.push(PARENT_PATH); - } - parts.push(a); - parts.extend(segments_a.by_ref()); - break; - } - } - } - if parts.is_empty() { - format!("./{}{}", last_a, &specifier[Position::AfterPath..]) - } else { - parts.push(last_a); - format!("{}{}", parts.join("/"), &specifier[Position::AfterPath..]) - } - } else { - specifier[Position::BeforePath..].into() - } - } else { - specifier[Position::BeforePath..].into() - } -} - #[cfg(test)] mod tests { use super::*; @@ -672,80 +576,6 @@ mod tests { } #[test] - fn test_relative_specifier() { - let fixtures: Vec<(&str, &str, &str)> = vec![ - ( - "https://deno.land/x/a/b/c.ts", - "https://deno.land/x/a/b/d.ts", - "./c.ts", - ), - ( - "https://deno.land/x/a/c.ts", - "https://deno.land/x/a/b/d.ts", - "../c.ts", - ), - ( - "https://deno.land/x/a/b/c/d.ts", - "https://deno.land/x/a/b/d.ts", - "./c/d.ts", - ), - ( - "https://deno.land/x/a/b/c/d.ts", - "https://deno.land/x/a/b/c/", - "./d.ts", - ), - ( - "https://deno.land/x/a/b/c/d/e.ts", - "https://deno.land/x/a/b/c/", - "./d/e.ts", - ), - ( - "https://deno.land/x/a/b/c/d/e.ts", - "https://deno.land/x/a/b/c/f.ts", - "./d/e.ts", - ), - ( - "https://deno.land/x/a/c.ts?foo=bar", - "https://deno.land/x/a/b/d.ts", - "../c.ts?foo=bar", - ), - ( - "https://deno.land/x/a/b/c.ts", - "https://deno.land/x/a/b/d.ts?foo=bar", - "./c.ts", - ), - #[cfg(not(windows))] - ("file:///a/b/c.ts", "file:///a/b/d.ts", "./c.ts"), - #[cfg(not(windows))] - ( - "file:///a/b/c.ts", - "https://deno.land/x/a/b/c.ts", - "/a/b/c.ts", - ), - ( - "https://deno.land/x/a/b/c.ts", - "https://deno.land/", - "/x/a/b/c.ts", - ), - ( - "https://deno.land/x/a/b/c.ts", - "https://deno.land/x/d/e/f.ts", - "../../a/b/c.ts", - ), - ]; - for (specifier_str, base_str, expected) in fixtures { - let specifier = resolve_url(specifier_str).unwrap(); - let base = resolve_url(base_str).unwrap(); - let actual = relative_specifier(&specifier, &base); - assert_eq!( - actual, expected, - "specifier: \"{}\" base: \"{}\"", - specifier_str, base_str - ); - } - } - - #[test] fn test_get_local_completions() { let temp_dir = TempDir::new(); let fixtures = temp_dir.path().join("fixtures"); diff --git a/cli/lsp/tsc.rs b/cli/lsp/tsc.rs index c402d2ac4..4bb5ae5f9 100644 --- a/cli/lsp/tsc.rs +++ b/cli/lsp/tsc.rs @@ -1,7 +1,6 @@ // Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. use super::code_lens; -use super::completions::relative_specifier; use super::config; use super::documents::AssetOrDocument; use super::language_server; @@ -19,6 +18,7 @@ use super::urls::LspUrlMap; use super::urls::INVALID_SPECIFIER; use crate::args::TsConfig; +use crate::fs_util::relative_specifier; use crate::fs_util::specifier_to_file_path; use crate::tsc; use crate::tsc::ResolveArgs; @@ -2098,11 +2098,13 @@ fn update_import_statement( { if let Ok(import_specifier) = normalize_specifier(&import_data.file_name) { - let new_module_specifier = - relative_specifier(&import_specifier, &item_data.specifier); - text_edit.new_text = text_edit - .new_text - .replace(&import_data.module_specifier, &new_module_specifier); + if let Some(new_module_specifier) = + relative_specifier(&item_data.specifier, &import_specifier) + { + text_edit.new_text = text_edit + .new_text + .replace(&import_data.module_specifier, &new_module_specifier); + } } } } |