diff options
author | Luca Casonato <hello@lcas.dev> | 2024-01-23 16:37:43 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-01-23 16:37:43 +0100 |
commit | 137f1a0c6836b50292c53e15aa85bd56ad14a943 (patch) | |
tree | c0bf018dbacee30ca80817ffc82751b6f9870fa0 /cli/tools/doc.rs | |
parent | 427b73c3ec1e01ca8c670d403a85fcf31777d253 (diff) |
feat(cli): improved diagnostics printing (#22049)
This initially uses the new diagnostic printer in `deno lint`,
`deno doc` and `deno publish`. In the limit we should also update
`deno check` to use this printer.
Diffstat (limited to 'cli/tools/doc.rs')
-rw-r--r-- | cli/tools/doc.rs | 143 |
1 files changed, 131 insertions, 12 deletions
diff --git a/cli/tools/doc.rs b/cli/tools/doc.rs index 4a59ec986..2cb9ddfba 100644 --- a/cli/tools/doc.rs +++ b/cli/tools/doc.rs @@ -5,6 +5,16 @@ use crate::args::DocHtmlFlag; use crate::args::DocSourceFileFlag; use crate::args::Flags; use crate::colors; +use crate::diagnostics::Diagnostic; +use crate::diagnostics::DiagnosticLevel; +use crate::diagnostics::DiagnosticLocation; +use crate::diagnostics::DiagnosticSnippet; +use crate::diagnostics::DiagnosticSnippetHighlight; +use crate::diagnostics::DiagnosticSnippetHighlightStyle; +use crate::diagnostics::DiagnosticSnippetSource; +use crate::diagnostics::DiagnosticSourcePos; +use crate::diagnostics::DiagnosticSourceRange; +use crate::diagnostics::SourceTextParsedSourceStore; use crate::display::write_json_to_stdout; use crate::display::write_to_stdout_ignore_sigpipe; use crate::factory::CliFactory; @@ -23,7 +33,10 @@ use deno_graph::ModuleAnalyzer; use deno_graph::ModuleParser; use deno_graph::ModuleSpecifier; use doc::DocDiagnostic; +use doc::DocDiagnosticKind; use indexmap::IndexMap; +use lsp_types::Url; +use std::borrow::Cow; use std::collections::BTreeMap; use std::rc::Rc; @@ -129,7 +142,7 @@ pub async fn doc(flags: Flags, doc_flags: DocFlags) -> Result<(), AnyError> { if doc_flags.lint { let diagnostics = doc_parser.take_diagnostics(); - check_diagnostics(&diagnostics)?; + check_diagnostics(&**parsed_source_cache, &diagnostics)?; } doc_nodes_by_url @@ -291,7 +304,118 @@ fn print_docs_to_stdout( write_to_stdout_ignore_sigpipe(details.as_bytes()).map_err(AnyError::from) } -fn check_diagnostics(diagnostics: &[DocDiagnostic]) -> Result<(), AnyError> { +impl Diagnostic for DocDiagnostic { + fn level(&self) -> DiagnosticLevel { + DiagnosticLevel::Error + } + + fn code(&self) -> impl std::fmt::Display + '_ { + match self.kind { + DocDiagnosticKind::MissingJsDoc => "missing-jsdoc", + DocDiagnosticKind::MissingExplicitType => "missing-explicit-type", + DocDiagnosticKind::MissingReturnType => "missing-return-type", + DocDiagnosticKind::PrivateTypeRef { .. } => "private-type-ref", + } + } + + fn message(&self) -> impl std::fmt::Display + '_ { + match &self.kind { + DocDiagnosticKind::MissingJsDoc => { + Cow::Borrowed("exported symbol is missing JSDoc documentation") + } + DocDiagnosticKind::MissingExplicitType => { + Cow::Borrowed("exported symbol is missing an explicit type annotation") + } + DocDiagnosticKind::MissingReturnType => Cow::Borrowed( + "exported function is missing an explicit return type annotation", + ), + DocDiagnosticKind::PrivateTypeRef { + reference, name, .. + } => Cow::Owned(format!( + "public type '{name}' references private type '{reference}'", + )), + } + } + + fn location(&self) -> DiagnosticLocation { + let specifier = Url::parse(&self.location.filename).unwrap(); + DiagnosticLocation::PositionInFile { + specifier: Cow::Owned(specifier), + source_pos: DiagnosticSourcePos::ByteIndex(self.location.byte_index), + } + } + + fn snippet(&self) -> Option<DiagnosticSnippet<'_>> { + let specifier = Url::parse(&self.location.filename).unwrap(); + Some(DiagnosticSnippet { + source: DiagnosticSnippetSource::Specifier(Cow::Owned(specifier)), + highlight: DiagnosticSnippetHighlight { + style: DiagnosticSnippetHighlightStyle::Error, + range: DiagnosticSourceRange { + start: DiagnosticSourcePos::ByteIndex(self.location.byte_index), + end: DiagnosticSourcePos::ByteIndex(self.location.byte_index + 1), + }, + description: None, + }, + }) + } + + fn hint(&self) -> Option<impl std::fmt::Display + '_> { + match &self.kind { + DocDiagnosticKind::PrivateTypeRef { .. } => { + Some("make the referenced type public or remove the reference") + } + _ => None, + } + } + fn snippet_fixed(&self) -> Option<DiagnosticSnippet<'_>> { + match &self.kind { + DocDiagnosticKind::PrivateTypeRef { + reference_location, .. + } => { + let specifier = Url::parse(&reference_location.filename).unwrap(); + Some(DiagnosticSnippet { + source: DiagnosticSnippetSource::Specifier(Cow::Owned(specifier)), + highlight: DiagnosticSnippetHighlight { + style: DiagnosticSnippetHighlightStyle::Hint, + range: DiagnosticSourceRange { + start: DiagnosticSourcePos::ByteIndex( + reference_location.byte_index, + ), + end: DiagnosticSourcePos::ByteIndex( + reference_location.byte_index + 1, + ), + }, + description: Some(Cow::Borrowed("this is the referenced type")), + }, + }) + } + _ => None, + } + } + + fn info(&self) -> std::borrow::Cow<'_, [std::borrow::Cow<'_, str>]> { + match &self.kind { + DocDiagnosticKind::MissingJsDoc => Cow::Borrowed(&[]), + DocDiagnosticKind::MissingExplicitType => Cow::Borrowed(&[]), + DocDiagnosticKind::MissingReturnType => Cow::Borrowed(&[]), + DocDiagnosticKind::PrivateTypeRef { .. } => { + Cow::Borrowed(&[Cow::Borrowed( + "to ensure documentation is complete all types that are exposed in the public API must be public", + )]) + } + } + } + + fn docs_url(&self) -> Option<impl std::fmt::Display + '_> { + None::<&str> + } +} + +fn check_diagnostics( + parsed_source_cache: &dyn deno_graph::ParsedSourceStore, + diagnostics: &[DocDiagnostic], +) -> Result<(), AnyError> { if diagnostics.is_empty() { return Ok(()); } @@ -309,18 +433,13 @@ fn check_diagnostics(diagnostics: &[DocDiagnostic]) -> Result<(), AnyError> { .push(diagnostic); } - for (filename, diagnostics_by_lc) in diagnostic_groups { - for (line, diagnostics_by_col) in diagnostics_by_lc { - for (col, diagnostics) in diagnostics_by_col { + for (_, diagnostics_by_lc) in diagnostic_groups { + for (_, diagnostics_by_col) in diagnostics_by_lc { + for (_, diagnostics) in diagnostics_by_col { for diagnostic in diagnostics { - log::warn!("{}", diagnostic.message()); + let sources = SourceTextParsedSourceStore(parsed_source_cache); + eprintln!("{}", diagnostic.display(&sources)); } - log::warn!( - " at {}:{}:{}\n", - colors::cyan(filename.as_str()), - colors::yellow(&line.to_string()), - colors::yellow(&(col + 1).to_string()) - ) } } } |