summaryrefslogtreecommitdiff
path: root/cli/lsp/diagnostics.rs
diff options
context:
space:
mode:
Diffstat (limited to 'cli/lsp/diagnostics.rs')
-rw-r--r--cli/lsp/diagnostics.rs81
1 files changed, 66 insertions, 15 deletions
diff --git a/cli/lsp/diagnostics.rs b/cli/lsp/diagnostics.rs
index 4faff2fae..9f3c409cc 100644
--- a/cli/lsp/diagnostics.rs
+++ b/cli/lsp/diagnostics.rs
@@ -13,6 +13,7 @@ use super::tsc;
use super::tsc::TsServer;
use crate::args::LintOptions;
+use crate::graph_util::enhanced_resolution_error_message;
use crate::node;
use crate::npm::NpmPackageReference;
use crate::tools::lint::get_configured_rules;
@@ -25,7 +26,9 @@ use deno_core::serde::Deserialize;
use deno_core::serde_json;
use deno_core::serde_json::json;
use deno_core::ModuleSpecifier;
+use deno_graph::ResolutionError;
use deno_graph::Resolved;
+use deno_graph::SpecifierError;
use deno_lint::rules::LintRule;
use deno_runtime::tokio_util::create_basic_runtime;
use log::error;
@@ -574,6 +577,12 @@ struct DiagnosticDataSpecifier {
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
+struct DiagnosticDataStrSpecifier {
+ pub specifier: String,
+}
+
+#[derive(Debug, Deserialize)]
+#[serde(rename_all = "camelCase")]
struct DiagnosticDataRedirect {
pub redirect: ModuleSpecifier,
}
@@ -621,9 +630,6 @@ pub enum DenoDiagnostic {
impl DenoDiagnostic {
fn code(&self) -> &str {
- use deno_graph::ResolutionError;
- use deno_graph::SpecifierError;
-
match self {
Self::DenoWarn(_) => "deno-warn",
Self::ImportMapRemap { .. } => "import-map-remap",
@@ -749,6 +755,32 @@ impl DenoDiagnostic {
..Default::default()
}
}
+ "import-prefix-missing" => {
+ // if an import-prefix-missing diagnostic ends up here, then that currently
+ // will only ever occur for a possible "node:" specifier, so don't bother
+ // checking if it's actually a "node:"" specifier
+ let data = diagnostic
+ .data
+ .clone()
+ .ok_or_else(|| anyhow!("Diagnostic is missing data"))?;
+ let data: DiagnosticDataStrSpecifier = serde_json::from_value(data)?;
+ lsp::CodeAction {
+ title: format!("Update specifier to node:{}", data.specifier),
+ kind: Some(lsp::CodeActionKind::QUICKFIX),
+ diagnostics: Some(vec![diagnostic.clone()]),
+ edit: Some(lsp::WorkspaceEdit {
+ changes: Some(HashMap::from([(
+ specifier.clone(),
+ vec![lsp::TextEdit {
+ new_text: format!("\"node:{}\"", data.specifier),
+ range: diagnostic.range,
+ }],
+ )])),
+ ..Default::default()
+ }),
+ ..Default::default()
+ }
+ }
_ => {
return Err(anyhow!(
"Unsupported diagnostic code (\"{}\") provided.",
@@ -764,17 +796,21 @@ impl DenoDiagnostic {
/// Given a reference to the code from an LSP diagnostic, determine if the
/// diagnostic is fixable or not
- pub fn is_fixable(code: &Option<lsp::NumberOrString>) -> bool {
- if let Some(lsp::NumberOrString::String(code)) = code {
- matches!(
- code.as_str(),
- "import-map-remap"
- | "no-cache"
- | "no-cache-npm"
- | "no-cache-data"
- | "no-assert-type"
- | "redirect"
- )
+ pub fn is_fixable(diagnostic: &lsp_types::Diagnostic) -> bool {
+ if let Some(lsp::NumberOrString::String(code)) = &diagnostic.code {
+ if code == "import-prefix-missing" {
+ diagnostic.data.is_some()
+ } else {
+ matches!(
+ code.as_str(),
+ "import-map-remap"
+ | "no-cache"
+ | "no-cache-npm"
+ | "no-cache-data"
+ | "no-assert-type"
+ | "redirect"
+ )
+ }
} else {
false
}
@@ -794,7 +830,22 @@ impl DenoDiagnostic {
Self::NoCacheNpm(pkg_ref, specifier) => (lsp::DiagnosticSeverity::ERROR, format!("Uncached or missing npm package: \"{}\".", pkg_ref.req), Some(json!({ "specifier": specifier }))),
Self::NoLocal(specifier) => (lsp::DiagnosticSeverity::ERROR, format!("Unable to load a local module: \"{}\".\n Please check the file path.", specifier), None),
Self::Redirect { from, to} => (lsp::DiagnosticSeverity::INFORMATION, format!("The import of \"{}\" was redirected to \"{}\".", from, to), Some(json!({ "specifier": from, "redirect": to }))),
- Self::ResolutionError(err) => (lsp::DiagnosticSeverity::ERROR, err.to_string(), None),
+ Self::ResolutionError(err) => (
+ lsp::DiagnosticSeverity::ERROR,
+ enhanced_resolution_error_message(err),
+ if let ResolutionError::InvalidSpecifier {
+ error: SpecifierError::ImportPrefixMissing(specifier, _),
+ ..
+ } = err {
+ if crate::node::resolve_builtin_node_module(specifier).is_ok() {
+ Some(json!({ "specifier": specifier }))
+ } else {
+ None
+ }
+ } else {
+ None
+ },
+ ),
Self::InvalidNodeSpecifier(specifier) => (lsp::DiagnosticSeverity::ERROR, format!("Unknown Node built-in module: {}", specifier.path()), None),
};
lsp::Diagnostic {