summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cli/fs_util.rs112
-rw-r--r--cli/lsp/completions.rs176
-rw-r--r--cli/lsp/tsc.rs14
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);
+ }
}
}
}