diff options
author | David Sherret <dsherret@users.noreply.github.com> | 2023-12-08 09:57:06 -0500 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-12-08 09:57:06 -0500 |
commit | ddfbe71cedbfe2ac31dbc7dbcf25761e5a7a1dce (patch) | |
tree | c25bc149f07c8f19b8cbf9029d97c7135425f57c /cli/resolver.rs | |
parent | 6596912d5ae824bf68452f824a20e74d58a2e365 (diff) |
feat(lsp): provide quick fixes for specifiers that could be resolved sloppily (#21506)
Diffstat (limited to 'cli/resolver.rs')
-rw-r--r-- | cli/resolver.rs | 133 |
1 files changed, 118 insertions, 15 deletions
diff --git a/cli/resolver.rs b/cli/resolver.rs index 6fd034979..45a7e865b 100644 --- a/cli/resolver.rs +++ b/cli/resolver.rs @@ -441,7 +441,7 @@ fn sloppy_imports_resolve( format_range_with_colors(referrer_range) }, ); - resolution.into_owned_specifier() + resolution.into_specifier().into_owned() } fn resolve_package_json_dep( @@ -562,15 +562,11 @@ impl SloppyImportsStatCache { return *entry; } - let entry = self.fs.stat_sync(path).ok().and_then(|stat| { - if stat.is_file { - Some(SloppyImportsFsEntry::File) - } else if stat.is_directory { - Some(SloppyImportsFsEntry::Dir) - } else { - None - } - }); + let entry = self + .fs + .stat_sync(path) + .ok() + .and_then(|stat| SloppyImportsFsEntry::from_fs_stat(&stat)); cache.insert(path.to_owned(), entry); entry } @@ -582,6 +578,20 @@ pub enum SloppyImportsFsEntry { Dir, } +impl SloppyImportsFsEntry { + pub fn from_fs_stat( + stat: &deno_runtime::deno_io::fs::FsStat, + ) -> Option<SloppyImportsFsEntry> { + if stat.is_file { + Some(SloppyImportsFsEntry::File) + } else if stat.is_directory { + Some(SloppyImportsFsEntry::Dir) + } else { + None + } + } +} + #[derive(Debug, PartialEq, Eq)] pub enum SloppyImportsResolution<'a> { /// No sloppy resolution was found. @@ -595,6 +605,15 @@ pub enum SloppyImportsResolution<'a> { } impl<'a> SloppyImportsResolution<'a> { + pub fn as_specifier(&self) -> &ModuleSpecifier { + match self { + Self::None(specifier) => specifier, + Self::JsToTs(specifier) => specifier, + Self::NoExtension(specifier) => specifier, + Self::Directory(specifier) => specifier, + } + } + pub fn into_specifier(self) -> Cow<'a, ModuleSpecifier> { match self { Self::None(specifier) => Cow::Borrowed(specifier), @@ -604,12 +623,48 @@ impl<'a> SloppyImportsResolution<'a> { } } - pub fn into_owned_specifier(self) -> ModuleSpecifier { + pub fn as_suggestion_message(&self) -> Option<String> { + Some(format!("Maybe {}", self.as_base_message()?)) + } + + pub fn as_lsp_quick_fix_message(&self) -> Option<String> { + let message = self.as_base_message()?; + let mut chars = message.chars(); + Some(format!( + "{}{}.", + chars.next().unwrap().to_uppercase(), + chars.as_str() + )) + } + + fn as_base_message(&self) -> Option<String> { match self { - Self::None(specifier) => specifier.clone(), - Self::JsToTs(specifier) => specifier, - Self::NoExtension(specifier) => specifier, - Self::Directory(specifier) => specifier, + SloppyImportsResolution::None(_) => None, + SloppyImportsResolution::JsToTs(specifier) => { + let media_type = MediaType::from_specifier(specifier); + Some(format!( + "change the extension to '{}'", + media_type.as_ts_extension() + )) + } + SloppyImportsResolution::NoExtension(specifier) => { + let media_type = MediaType::from_specifier(specifier); + Some(format!( + "add a '{}' extension", + media_type.as_ts_extension() + )) + } + SloppyImportsResolution::Directory(specifier) => { + let file_name = specifier + .path() + .rsplit_once('/') + .map(|(_, file_name)| file_name) + .unwrap_or(specifier.path()); + Some(format!( + "specify path to '{}' file in directory instead", + file_name + )) + } } } } @@ -626,6 +681,17 @@ impl SloppyImportsResolver { } } + pub fn resolve_with_fs<'a>( + fs: &dyn FileSystem, + specifier: &'a ModuleSpecifier, + ) -> SloppyImportsResolution<'a> { + Self::resolve_with_stat_sync(specifier, |path| { + fs.stat_sync(path) + .ok() + .and_then(|stat| SloppyImportsFsEntry::from_fs_stat(&stat)) + }) + } + pub fn resolve_with_stat_sync( specifier: &ModuleSpecifier, stat_sync: impl Fn(&Path) -> Option<SloppyImportsFsEntry>, @@ -885,4 +951,41 @@ mod test { ); } } + + #[test] + fn test_sloppy_import_resolution_suggestion_message() { + // none + let url = ModuleSpecifier::parse("file:///dir/index.js").unwrap(); + assert_eq!( + SloppyImportsResolution::None(&url).as_suggestion_message(), + None, + ); + // directory + assert_eq!( + SloppyImportsResolution::Directory( + ModuleSpecifier::parse("file:///dir/index.js").unwrap() + ) + .as_suggestion_message() + .unwrap(), + "Maybe specify path to 'index.js' file in directory instead" + ); + // no ext + assert_eq!( + SloppyImportsResolution::NoExtension( + ModuleSpecifier::parse("file:///dir/index.mjs").unwrap() + ) + .as_suggestion_message() + .unwrap(), + "Maybe add a '.mjs' extension" + ); + // js to ts + assert_eq!( + SloppyImportsResolution::JsToTs( + ModuleSpecifier::parse("file:///dir/index.mts").unwrap() + ) + .as_suggestion_message() + .unwrap(), + "Maybe change the extension to '.mts'" + ); + } } |