mirror of
https://github.com/denoland/deno.git
synced 2024-12-25 08:39:09 -05:00
3374c73fba
- Add more support for generics - Add the --private flag - displays documentation for not exported and private nodes - Display more attributes like abstract, static and readonly - Display type aliases - Refactor module to use the Display trait - Use a bit more color
474 lines
12 KiB
Rust
474 lines
12 KiB
Rust
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
|
|
|
// TODO(ry) This module builds up output by appending to a string. Instead it
|
|
// should either use a formatting trait
|
|
// https://doc.rust-lang.org/std/fmt/index.html#formatting-traits
|
|
// Or perhaps implement a Serializer for serde
|
|
// https://docs.serde.rs/serde/ser/trait.Serializer.html
|
|
|
|
// TODO(ry) The methods in this module take ownership of the DocNodes, this is
|
|
// unnecessary and can result in unnecessary copying. Instead they should take
|
|
// references.
|
|
|
|
use crate::colors;
|
|
use crate::doc;
|
|
use crate::doc::display::{
|
|
display_abstract, display_async, display_generator, Indent, SliceDisplayer,
|
|
};
|
|
use crate::doc::DocNodeKind;
|
|
use crate::swc_ecma_ast;
|
|
use std::fmt::{Display, Formatter, Result as FmtResult};
|
|
|
|
pub struct DocPrinter<'a> {
|
|
doc_nodes: &'a [doc::DocNode],
|
|
details: bool,
|
|
private: bool,
|
|
}
|
|
|
|
impl<'a> DocPrinter<'a> {
|
|
pub fn new(
|
|
doc_nodes: &[doc::DocNode],
|
|
details: bool,
|
|
private: bool,
|
|
) -> DocPrinter {
|
|
DocPrinter {
|
|
doc_nodes,
|
|
details,
|
|
private,
|
|
}
|
|
}
|
|
|
|
pub fn format(&self, w: &mut Formatter<'_>) -> FmtResult {
|
|
if self.details {
|
|
self.format_details(w, self.doc_nodes, 0)
|
|
} else {
|
|
self.format_summary(w, self.doc_nodes, 0)
|
|
}
|
|
}
|
|
|
|
fn format_summary(
|
|
&self,
|
|
w: &mut Formatter<'_>,
|
|
doc_nodes: &[doc::DocNode],
|
|
indent: i64,
|
|
) -> FmtResult {
|
|
let mut sorted = Vec::from(doc_nodes);
|
|
sorted.sort_unstable_by(|a, b| {
|
|
let kind_cmp = self.kind_order(&a.kind).cmp(&self.kind_order(&b.kind));
|
|
if kind_cmp == core::cmp::Ordering::Equal {
|
|
a.name.cmp(&b.name)
|
|
} else {
|
|
kind_cmp
|
|
}
|
|
});
|
|
|
|
for node in sorted {
|
|
self.format_signature(w, &node, indent)?;
|
|
|
|
if let Some(js_doc) = &node.js_doc {
|
|
self.format_jsdoc(w, js_doc, indent + 1, self.details)?;
|
|
}
|
|
|
|
writeln!(w)?;
|
|
|
|
if DocNodeKind::Namespace == node.kind {
|
|
self.format_summary(
|
|
w,
|
|
&node.namespace_def.as_ref().unwrap().elements,
|
|
indent + 1,
|
|
)?;
|
|
|
|
writeln!(w)?;
|
|
};
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
fn format_details(
|
|
&self,
|
|
w: &mut Formatter<'_>,
|
|
doc_nodes: &[doc::DocNode],
|
|
indent: i64,
|
|
) -> FmtResult {
|
|
for node in doc_nodes {
|
|
write!(
|
|
w,
|
|
"{}",
|
|
colors::italic_gray(&format!(
|
|
"Defined in {}:{}:{} \n\n",
|
|
node.location.filename, node.location.line, node.location.col
|
|
))
|
|
)?;
|
|
|
|
self.format_signature(w, &node, indent)?;
|
|
|
|
let js_doc = &node.js_doc;
|
|
if let Some(js_doc) = js_doc {
|
|
self.format_jsdoc(w, js_doc, indent + 1, self.details)?;
|
|
}
|
|
writeln!(w)?;
|
|
|
|
match node.kind {
|
|
DocNodeKind::Class => self.format_class_details(w, node)?,
|
|
DocNodeKind::Enum => self.format_enum_details(w, node)?,
|
|
DocNodeKind::Interface => self.format_interface_details(w, node)?,
|
|
DocNodeKind::Namespace => self.format_namespace_details(w, node)?,
|
|
_ => {}
|
|
}
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
fn kind_order(&self, kind: &doc::DocNodeKind) -> i64 {
|
|
match kind {
|
|
DocNodeKind::Function => 0,
|
|
DocNodeKind::Variable => 1,
|
|
DocNodeKind::Class => 2,
|
|
DocNodeKind::Enum => 3,
|
|
DocNodeKind::Interface => 4,
|
|
DocNodeKind::TypeAlias => 5,
|
|
DocNodeKind::Namespace => 6,
|
|
}
|
|
}
|
|
|
|
fn format_signature(
|
|
&self,
|
|
w: &mut Formatter<'_>,
|
|
node: &doc::DocNode,
|
|
indent: i64,
|
|
) -> FmtResult {
|
|
match node.kind {
|
|
DocNodeKind::Function => self.format_function_signature(w, node, indent),
|
|
DocNodeKind::Variable => self.format_variable_signature(w, node, indent),
|
|
DocNodeKind::Class => self.format_class_signature(w, node, indent),
|
|
DocNodeKind::Enum => self.format_enum_signature(w, node, indent),
|
|
DocNodeKind::Interface => {
|
|
self.format_interface_signature(w, node, indent)
|
|
}
|
|
DocNodeKind::TypeAlias => {
|
|
self.format_type_alias_signature(w, node, indent)
|
|
}
|
|
DocNodeKind::Namespace => {
|
|
self.format_namespace_signature(w, node, indent)
|
|
}
|
|
}
|
|
}
|
|
|
|
// TODO(SyrupThinker) this should use a JSDoc parser
|
|
fn format_jsdoc(
|
|
&self,
|
|
w: &mut Formatter<'_>,
|
|
jsdoc: &str,
|
|
indent: i64,
|
|
details: bool,
|
|
) -> FmtResult {
|
|
for line in jsdoc.lines() {
|
|
// Only show the first paragraph when summarising
|
|
// This should use the @summary JSDoc tag instead
|
|
if !details && line.is_empty() {
|
|
break;
|
|
}
|
|
|
|
writeln!(w, "{}{}", Indent(indent), colors::gray(&line))?;
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
fn format_class_details(
|
|
&self,
|
|
w: &mut Formatter<'_>,
|
|
node: &doc::DocNode,
|
|
) -> FmtResult {
|
|
let class_def = node.class_def.as_ref().unwrap();
|
|
for node in &class_def.constructors {
|
|
writeln!(w, "{}{}", Indent(1), node,)?;
|
|
if let Some(js_doc) = &node.js_doc {
|
|
self.format_jsdoc(w, &js_doc, 2, self.details)?;
|
|
}
|
|
}
|
|
for node in class_def.properties.iter().filter(|node| {
|
|
self.private
|
|
|| node
|
|
.accessibility
|
|
.unwrap_or(swc_ecma_ast::Accessibility::Public)
|
|
!= swc_ecma_ast::Accessibility::Private
|
|
}) {
|
|
writeln!(w, "{}{}", Indent(1), node,)?;
|
|
if let Some(js_doc) = &node.js_doc {
|
|
self.format_jsdoc(w, &js_doc, 2, self.details)?;
|
|
}
|
|
}
|
|
for index_sign_def in &class_def.index_signatures {
|
|
writeln!(w, "{}{}", Indent(1), index_sign_def)?;
|
|
}
|
|
for node in class_def.methods.iter().filter(|node| {
|
|
self.private
|
|
|| node
|
|
.accessibility
|
|
.unwrap_or(swc_ecma_ast::Accessibility::Public)
|
|
!= swc_ecma_ast::Accessibility::Private
|
|
}) {
|
|
writeln!(w, "{}{}", Indent(1), node,)?;
|
|
if let Some(js_doc) = &node.js_doc {
|
|
self.format_jsdoc(w, js_doc, 2, self.details)?;
|
|
}
|
|
}
|
|
writeln!(w)
|
|
}
|
|
|
|
fn format_enum_details(
|
|
&self,
|
|
w: &mut Formatter<'_>,
|
|
node: &doc::DocNode,
|
|
) -> FmtResult {
|
|
let enum_def = node.enum_def.as_ref().unwrap();
|
|
for member in &enum_def.members {
|
|
writeln!(w, "{}{}", Indent(1), colors::bold(&member.name))?;
|
|
}
|
|
writeln!(w)
|
|
}
|
|
|
|
fn format_interface_details(
|
|
&self,
|
|
w: &mut Formatter<'_>,
|
|
node: &doc::DocNode,
|
|
) -> FmtResult {
|
|
let interface_def = node.interface_def.as_ref().unwrap();
|
|
|
|
for property_def in &interface_def.properties {
|
|
writeln!(w, "{}{}", Indent(1), property_def)?;
|
|
if let Some(js_doc) = &property_def.js_doc {
|
|
self.format_jsdoc(w, js_doc, 2, self.details)?;
|
|
}
|
|
}
|
|
for method_def in &interface_def.methods {
|
|
writeln!(w, "{}{}", Indent(1), method_def)?;
|
|
if let Some(js_doc) = &method_def.js_doc {
|
|
self.format_jsdoc(w, js_doc, 2, self.details)?;
|
|
}
|
|
}
|
|
for index_sign_def in &interface_def.index_signatures {
|
|
writeln!(w, "{}{}", Indent(1), index_sign_def)?;
|
|
}
|
|
writeln!(w)
|
|
}
|
|
|
|
fn format_namespace_details(
|
|
&self,
|
|
w: &mut Formatter<'_>,
|
|
node: &doc::DocNode,
|
|
) -> FmtResult {
|
|
let elements = &node.namespace_def.as_ref().unwrap().elements;
|
|
for node in elements {
|
|
self.format_signature(w, &node, 1)?;
|
|
if let Some(js_doc) = &node.js_doc {
|
|
self.format_jsdoc(w, js_doc, 2, false)?;
|
|
}
|
|
}
|
|
writeln!(w)
|
|
}
|
|
|
|
fn format_class_signature(
|
|
&self,
|
|
w: &mut Formatter<'_>,
|
|
node: &doc::DocNode,
|
|
indent: i64,
|
|
) -> FmtResult {
|
|
let class_def = node.class_def.as_ref().unwrap();
|
|
write!(
|
|
w,
|
|
"{}{}{} {}",
|
|
Indent(indent),
|
|
display_abstract(class_def.is_abstract),
|
|
colors::magenta("class"),
|
|
colors::bold(&node.name),
|
|
)?;
|
|
if !class_def.type_params.is_empty() {
|
|
write!(
|
|
w,
|
|
"<{}>",
|
|
SliceDisplayer::new(&class_def.type_params, ", ", false)
|
|
)?;
|
|
}
|
|
|
|
if let Some(extends) = &class_def.extends {
|
|
write!(w, " {} {}", colors::magenta("extends"), extends)?;
|
|
}
|
|
if !class_def.super_type_params.is_empty() {
|
|
write!(
|
|
w,
|
|
"<{}>",
|
|
SliceDisplayer::new(&class_def.super_type_params, ", ", false)
|
|
)?;
|
|
}
|
|
|
|
if !class_def.implements.is_empty() {
|
|
write!(
|
|
w,
|
|
" {} {}",
|
|
colors::magenta("implements"),
|
|
SliceDisplayer::new(&class_def.implements, ", ", false)
|
|
)?;
|
|
}
|
|
|
|
writeln!(w)
|
|
}
|
|
|
|
fn format_enum_signature(
|
|
&self,
|
|
w: &mut Formatter<'_>,
|
|
node: &doc::DocNode,
|
|
indent: i64,
|
|
) -> FmtResult {
|
|
writeln!(
|
|
w,
|
|
"{}{} {}",
|
|
Indent(indent),
|
|
colors::magenta("enum"),
|
|
colors::bold(&node.name)
|
|
)
|
|
}
|
|
|
|
fn format_function_signature(
|
|
&self,
|
|
w: &mut Formatter<'_>,
|
|
node: &doc::DocNode,
|
|
indent: i64,
|
|
) -> FmtResult {
|
|
let function_def = node.function_def.as_ref().unwrap();
|
|
write!(
|
|
w,
|
|
"{}{}{}{} {}",
|
|
Indent(indent),
|
|
display_async(function_def.is_async),
|
|
colors::magenta("function"),
|
|
display_generator(function_def.is_generator),
|
|
colors::bold(&node.name)
|
|
)?;
|
|
if !function_def.type_params.is_empty() {
|
|
write!(
|
|
w,
|
|
"<{}>",
|
|
SliceDisplayer::new(&function_def.type_params, ", ", false)
|
|
)?;
|
|
}
|
|
write!(
|
|
w,
|
|
"({})",
|
|
SliceDisplayer::new(&function_def.params, ", ", false)
|
|
)?;
|
|
if let Some(return_type) = &function_def.return_type {
|
|
write!(w, ": {}", return_type)?;
|
|
}
|
|
writeln!(w)
|
|
}
|
|
|
|
fn format_interface_signature(
|
|
&self,
|
|
w: &mut Formatter<'_>,
|
|
node: &doc::DocNode,
|
|
indent: i64,
|
|
) -> FmtResult {
|
|
let interface_def = node.interface_def.as_ref().unwrap();
|
|
write!(
|
|
w,
|
|
"{}{} {}",
|
|
Indent(indent),
|
|
colors::magenta("interface"),
|
|
colors::bold(&node.name)
|
|
)?;
|
|
|
|
if !interface_def.type_params.is_empty() {
|
|
write!(
|
|
w,
|
|
"<{}>",
|
|
SliceDisplayer::new(&interface_def.type_params, ", ", false)
|
|
)?;
|
|
}
|
|
|
|
if !interface_def.extends.is_empty() {
|
|
write!(
|
|
w,
|
|
" {} {}",
|
|
colors::magenta("extends"),
|
|
SliceDisplayer::new(&interface_def.extends, ", ", false)
|
|
)?;
|
|
}
|
|
|
|
writeln!(w)
|
|
}
|
|
|
|
fn format_type_alias_signature(
|
|
&self,
|
|
w: &mut Formatter<'_>,
|
|
node: &doc::DocNode,
|
|
indent: i64,
|
|
) -> FmtResult {
|
|
let type_alias_def = node.type_alias_def.as_ref().unwrap();
|
|
write!(
|
|
w,
|
|
"{}{} {}",
|
|
Indent(indent),
|
|
colors::magenta("type"),
|
|
colors::bold(&node.name),
|
|
)?;
|
|
|
|
if !type_alias_def.type_params.is_empty() {
|
|
write!(
|
|
w,
|
|
"<{}>",
|
|
SliceDisplayer::new(&type_alias_def.type_params, ", ", false)
|
|
)?;
|
|
}
|
|
|
|
writeln!(w, " = {}", type_alias_def.ts_type)
|
|
}
|
|
|
|
fn format_namespace_signature(
|
|
&self,
|
|
w: &mut Formatter<'_>,
|
|
node: &doc::DocNode,
|
|
indent: i64,
|
|
) -> FmtResult {
|
|
writeln!(
|
|
w,
|
|
"{}{} {}",
|
|
Indent(indent),
|
|
colors::magenta("namespace"),
|
|
colors::bold(&node.name)
|
|
)
|
|
}
|
|
|
|
fn format_variable_signature(
|
|
&self,
|
|
w: &mut Formatter<'_>,
|
|
node: &doc::DocNode,
|
|
indent: i64,
|
|
) -> FmtResult {
|
|
let variable_def = node.variable_def.as_ref().unwrap();
|
|
write!(
|
|
w,
|
|
"{}{} {}",
|
|
Indent(indent),
|
|
colors::magenta(match variable_def.kind {
|
|
swc_ecma_ast::VarDeclKind::Const => "const",
|
|
swc_ecma_ast::VarDeclKind::Let => "let",
|
|
swc_ecma_ast::VarDeclKind::Var => "var",
|
|
}),
|
|
colors::bold(&node.name),
|
|
)?;
|
|
if let Some(ts_type) = &variable_def.ts_type {
|
|
write!(w, ": {}", ts_type)?;
|
|
}
|
|
writeln!(w)
|
|
}
|
|
}
|
|
|
|
impl<'a> Display for DocPrinter<'a> {
|
|
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
|
|
self.format(f)
|
|
}
|
|
}
|