1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-11-22 15:06:54 -05:00

feat(doc): Improve terminal printer (#6594)

- 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
This commit is contained in:
Valentin Anger 2020-07-12 14:16:33 +02:00 committed by GitHub
parent 871f9255e3
commit 3374c73fba
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 2888 additions and 1743 deletions

View file

@ -3,7 +3,7 @@ use regex::Regex;
use std::env;
use std::fmt;
use std::io::Write;
use termcolor::Color::{Ansi256, Black, Magenta, Red, White};
use termcolor::Color::{Ansi256, Black, Blue, Green, Magenta, Red, White};
use termcolor::{Ansi, ColorSpec, WriteColor};
#[cfg(windows)]
@ -54,7 +54,10 @@ pub fn red_bold(s: &str) -> impl fmt::Display {
pub fn green_bold(s: &str) -> impl fmt::Display {
let mut style_spec = ColorSpec::new();
style_spec.set_fg(Some(Ansi256(10))).set_bold(true);
style_spec
.set_fg(Some(Green))
.set_bold(true)
.set_intense(true);
style(&s, style_spec)
}
@ -102,7 +105,7 @@ pub fn red(s: &str) -> impl fmt::Display {
pub fn green(s: &str) -> impl fmt::Display {
let mut style_spec = ColorSpec::new();
style_spec.set_fg(Some(Ansi256(10)));
style_spec.set_fg(Some(Green)).set_intense(true);
style(&s, style_spec)
}
@ -124,6 +127,12 @@ pub fn gray(s: &str) -> impl fmt::Display {
style(&s, style_spec)
}
pub fn italic_gray(s: &str) -> impl fmt::Display {
let mut style_spec = ColorSpec::new();
style_spec.set_fg(Some(Ansi256(8))).set_italic(true);
style(&s, style_spec)
}
pub fn italic_bold_gray(s: &str) -> impl fmt::Display {
let mut style_spec = ColorSpec::new();
style_spec
@ -132,3 +141,9 @@ pub fn italic_bold_gray(s: &str) -> impl fmt::Display {
.set_italic(true);
style(&s, style_spec)
}
pub fn intense_blue(s: &str) -> impl fmt::Display {
let mut style_spec = ColorSpec::new();
style_spec.set_fg(Some(Blue)).set_intense(true);
style(&s, style_spec)
}

View file

@ -1,5 +1,10 @@
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
use crate::swc_common::SourceMap;
use crate::colors;
use crate::doc::display::{
display_abstract, display_accessibility, display_async, display_generator,
display_method, display_optional, display_readonly, display_static,
SliceDisplayer,
};
use crate::swc_common::Spanned;
use crate::swc_ecma_ast;
use serde::Serialize;
@ -7,18 +12,21 @@ use serde::Serialize;
use super::function::function_to_function_def;
use super::function::FunctionDef;
use super::interface::expr_to_name;
use super::params::assign_pat_to_param_def;
use super::params::ident_to_param_def;
use super::params::pat_to_param_def;
use super::params::{
assign_pat_to_param_def, ident_to_param_def, pat_to_param_def,
prop_name_to_string, ts_fn_param_to_param_def,
};
use super::parser::DocParser;
use super::ts_type::ts_entity_name_to_name;
use super::ts_type::ts_type_ann_to_def;
use super::ts_type::TsTypeDef;
use super::ts_type::{
maybe_type_param_instantiation_to_type_defs, ts_type_ann_to_def, TsTypeDef,
};
use super::ts_type_param::maybe_type_param_decl_to_type_param_defs;
use super::ts_type_param::TsTypeParamDef;
use super::Location;
use super::ParamDef;
use std::fmt::{Display, Formatter, Result as FmtResult};
#[derive(Debug, Serialize, Clone)]
#[serde(rename_all = "camelCase")]
pub struct ClassConstructorDef {
@ -29,6 +37,18 @@ pub struct ClassConstructorDef {
pub location: Location,
}
impl Display for ClassConstructorDef {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
write!(
f,
"{}{}({})",
display_accessibility(self.accessibility),
colors::magenta("constructor"),
SliceDisplayer::new(&self.params, ", ", false),
)
}
}
#[derive(Debug, Serialize, Clone)]
#[serde(rename_all = "camelCase")]
pub struct ClassPropertyDef {
@ -43,6 +63,48 @@ pub struct ClassPropertyDef {
pub location: Location,
}
impl Display for ClassPropertyDef {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
write!(
f,
"{}{}{}{}{}{}",
display_abstract(self.is_abstract),
display_accessibility(self.accessibility),
display_static(self.is_static),
display_readonly(self.readonly),
colors::bold(&self.name),
display_optional(self.optional),
)?;
if let Some(ts_type) = &self.ts_type {
write!(f, ": {}", ts_type)?;
}
Ok(())
}
}
#[derive(Debug, Serialize, Clone)]
#[serde(rename_all = "camelCase")]
pub struct ClassIndexSignatureDef {
pub readonly: bool,
pub params: Vec<ParamDef>,
pub ts_type: Option<TsTypeDef>,
}
impl Display for ClassIndexSignatureDef {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
write!(
f,
"{}[{}]",
display_readonly(self.readonly),
SliceDisplayer::new(&self.params, ", ", false)
)?;
if let Some(ts_type) = &self.ts_type {
write!(f, ": {}", ts_type)?;
}
Ok(())
}
}
#[derive(Debug, Serialize, Clone)]
#[serde(rename_all = "camelCase")]
pub struct ClassMethodDef {
@ -57,32 +119,41 @@ pub struct ClassMethodDef {
pub location: Location,
}
impl Display for ClassMethodDef {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
write!(
f,
"{}{}{}{}{}{}{}{}({})",
display_abstract(self.is_abstract),
display_accessibility(self.accessibility),
display_static(self.is_static),
display_async(self.function_def.is_async),
display_method(self.kind),
display_generator(self.function_def.is_generator),
colors::bold(&self.name),
display_optional(self.optional),
SliceDisplayer::new(&self.function_def.params, ", ", false),
)?;
if let Some(return_type) = &self.function_def.return_type {
write!(f, ": {}", return_type)?;
}
Ok(())
}
}
#[derive(Debug, Serialize, Clone)]
#[serde(rename_all = "camelCase")]
pub struct ClassDef {
// TODO(bartlomieju): decorators, super_type_params
// TODO(bartlomieju): decorators
pub is_abstract: bool,
pub constructors: Vec<ClassConstructorDef>,
pub properties: Vec<ClassPropertyDef>,
pub index_signatures: Vec<ClassIndexSignatureDef>,
pub methods: Vec<ClassMethodDef>,
pub extends: Option<String>,
pub implements: Vec<String>,
pub implements: Vec<TsTypeDef>,
pub type_params: Vec<TsTypeParamDef>,
}
fn prop_name_to_string(
source_map: &SourceMap,
prop_name: &swc_ecma_ast::PropName,
) -> String {
use crate::swc_ecma_ast::PropName;
match prop_name {
PropName::Ident(ident) => ident.sym.to_string(),
PropName::Str(str_) => str_.value.to_string(),
PropName::Num(num) => num.value.to_string(),
PropName::Computed(comp_prop_name) => {
source_map.span_to_snippet(comp_prop_name.span).unwrap()
}
}
pub super_type_params: Vec<TsTypeDef>,
}
pub fn class_to_class_def(
@ -92,6 +163,7 @@ pub fn class_to_class_def(
let mut constructors = vec![];
let mut methods = vec![];
let mut properties = vec![];
let mut index_signatures = vec![];
let extends: Option<String> = match &class.super_class {
Some(boxed) => {
@ -105,11 +177,11 @@ pub fn class_to_class_def(
None => None,
};
let implements: Vec<String> = class
let implements = class
.implements
.iter()
.map(|expr| ts_entity_name_to_name(&expr.expr))
.collect();
.map(|expr| expr.into())
.collect::<Vec<TsTypeDef>>();
for member in &class.body {
use crate::swc_ecma_ast::ClassMember::*;
@ -117,8 +189,10 @@ pub fn class_to_class_def(
match member {
Constructor(ctor) => {
let ctor_js_doc = doc_parser.js_doc_for_span(ctor.span());
let constructor_name =
prop_name_to_string(&doc_parser.ast_parser.source_map, &ctor.key);
let constructor_name = prop_name_to_string(
&ctor.key,
Some(&doc_parser.ast_parser.source_map),
);
let mut params = vec![];
@ -126,14 +200,23 @@ pub fn class_to_class_def(
use crate::swc_ecma_ast::ParamOrTsParamProp::*;
let param_def = match param {
Param(param) => pat_to_param_def(&param.pat),
Param(param) => pat_to_param_def(
&param.pat,
Some(&doc_parser.ast_parser.source_map),
),
TsParamProp(ts_param_prop) => {
use swc_ecma_ast::TsParamPropParam;
match &ts_param_prop.param {
TsParamPropParam::Ident(ident) => ident_to_param_def(ident),
TsParamPropParam::Ident(ident) => ident_to_param_def(
ident,
Some(&doc_parser.ast_parser.source_map),
),
TsParamPropParam::Assign(assign_pat) => {
assign_pat_to_param_def(assign_pat)
assign_pat_to_param_def(
assign_pat,
Some(&doc_parser.ast_parser.source_map),
)
}
}
}
@ -153,10 +236,11 @@ pub fn class_to_class_def(
Method(class_method) => {
let method_js_doc = doc_parser.js_doc_for_span(class_method.span());
let method_name = prop_name_to_string(
&doc_parser.ast_parser.source_map,
&class_method.key,
Some(&doc_parser.ast_parser.source_map),
);
let fn_def = function_to_function_def(&class_method.function);
let fn_def =
function_to_function_def(&doc_parser, &class_method.function);
let method_def = ClassMethodDef {
js_doc: method_js_doc,
accessibility: class_method.accessibility,
@ -199,8 +283,26 @@ pub fn class_to_class_def(
};
properties.push(prop_def);
}
TsIndexSignature(ts_index_sig) => {
let mut params = vec![];
for param in &ts_index_sig.params {
let param_def = ts_fn_param_to_param_def(param, None);
params.push(param_def);
}
let ts_type = ts_index_sig
.type_ann
.as_ref()
.map(|rt| (&*rt.type_ann).into());
let index_sig_def = ClassIndexSignatureDef {
readonly: ts_index_sig.readonly,
params,
ts_type,
};
index_signatures.push(index_sig_def);
}
// TODO(bartlomieju):
TsIndexSignature(_) => {}
PrivateMethod(_) => {}
PrivateProp(_) => {}
_ => {}
@ -210,14 +312,20 @@ pub fn class_to_class_def(
let type_params =
maybe_type_param_decl_to_type_param_defs(class.type_params.as_ref());
let super_type_params = maybe_type_param_instantiation_to_type_defs(
class.super_type_params.as_ref(),
);
ClassDef {
is_abstract: class.is_abstract,
extends,
implements,
constructors,
properties,
index_signatures,
methods,
type_params,
super_type_params,
}
}

90
cli/doc/display.rs Normal file
View file

@ -0,0 +1,90 @@
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
use crate::colors;
use crate::swc_ecma_ast;
use std::fmt::{Display, Formatter, Result};
pub(crate) struct Indent(pub i64);
impl Display for Indent {
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
for _ in 0..self.0 {
write!(f, " ")?;
}
Ok(())
}
}
pub(crate) struct SliceDisplayer<'a, T: Display>(&'a [T], &'a str, bool);
impl<'a, T: Display> SliceDisplayer<'a, T> {
pub fn new(
slice: &'a [T],
separator: &'a str,
trailing: bool,
) -> SliceDisplayer<'a, T> {
SliceDisplayer(slice, separator, trailing)
}
}
impl<T: Display> Display for SliceDisplayer<'_, T> {
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
if self.0.is_empty() {
return Ok(());
}
write!(f, "{}", self.0[0])?;
for v in &self.0[1..] {
write!(f, "{}{}", self.1, v)?;
}
if self.2 {
write!(f, "{}", self.1)?;
}
Ok(())
}
}
pub(crate) fn display_abstract(is_abstract: bool) -> impl Display {
colors::magenta(if is_abstract { "abstract " } else { "" })
}
pub(crate) fn display_accessibility(
accessibility: Option<swc_ecma_ast::Accessibility>,
) -> impl Display {
colors::magenta(
match accessibility.unwrap_or(swc_ecma_ast::Accessibility::Public) {
swc_ecma_ast::Accessibility::Public => "",
swc_ecma_ast::Accessibility::Protected => "protected ",
swc_ecma_ast::Accessibility::Private => "private ",
},
)
}
pub(crate) fn display_async(is_async: bool) -> impl Display {
colors::magenta(if is_async { "async " } else { "" })
}
pub(crate) fn display_generator(is_generator: bool) -> impl Display {
colors::magenta(if is_generator { "*" } else { "" })
}
pub(crate) fn display_method(method: swc_ecma_ast::MethodKind) -> impl Display {
colors::magenta(match method {
swc_ecma_ast::MethodKind::Getter => "get ",
swc_ecma_ast::MethodKind::Setter => "set ",
_ => "",
})
}
pub(crate) fn display_optional(is_optional: bool) -> impl Display {
colors::magenta(if is_optional { "?" } else { "" })
}
pub(crate) fn display_readonly(is_readonly: bool) -> impl Display {
colors::magenta(if is_readonly { "readonly " } else { "" })
}
pub(crate) fn display_static(is_static: bool) -> impl Display {
colors::magenta(if is_static { "static " } else { "" })
}

View file

@ -1,5 +1,6 @@
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
use super::params::pat_to_param_def;
use super::parser::DocParser;
use super::ts_type::ts_type_ann_to_def;
use super::ts_type::TsTypeDef;
use super::ts_type_param::maybe_type_param_decl_to_type_param_defs;
@ -20,12 +21,14 @@ pub struct FunctionDef {
}
pub fn function_to_function_def(
doc_parser: &DocParser,
function: &swc_ecma_ast::Function,
) -> FunctionDef {
let mut params = vec![];
for param in &function.params {
let param_def = pat_to_param_def(&param.pat);
let param_def =
pat_to_param_def(&param.pat, Some(&doc_parser.ast_parser.source_map));
params.push(param_def);
}
@ -47,9 +50,10 @@ pub fn function_to_function_def(
}
pub fn get_doc_for_fn_decl(
doc_parser: &DocParser,
fn_decl: &swc_ecma_ast::FnDecl,
) -> (String, FunctionDef) {
let name = fn_decl.ident.sym.to_string();
let fn_def = function_to_function_def(&fn_decl.function);
let fn_def = function_to_function_def(&doc_parser, &fn_decl.function);
(name, fn_def)
}

View file

@ -1,10 +1,11 @@
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
use crate::colors;
use crate::doc::display::{display_optional, display_readonly, SliceDisplayer};
use crate::swc_ecma_ast;
use serde::Serialize;
use super::params::ts_fn_param_to_param_def;
use super::parser::DocParser;
use super::ts_type::ts_entity_name_to_name;
use super::ts_type::ts_type_ann_to_def;
use super::ts_type::TsTypeDef;
use super::ts_type_param::maybe_type_param_decl_to_type_param_defs;
@ -12,6 +13,8 @@ use super::ts_type_param::TsTypeParamDef;
use super::Location;
use super::ParamDef;
use std::fmt::{Display, Formatter, Result as FmtResult};
#[derive(Debug, Serialize, Clone)]
#[serde(rename_all = "camelCase")]
pub struct InterfaceMethodDef {
@ -24,6 +27,22 @@ pub struct InterfaceMethodDef {
pub type_params: Vec<TsTypeParamDef>,
}
impl Display for InterfaceMethodDef {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
write!(
f,
"{}{}({})",
colors::bold(&self.name),
display_optional(self.optional),
SliceDisplayer::new(&self.params, ", ", false),
)?;
if let Some(return_type) = &self.return_type {
write!(f, ": {}", return_type)?;
}
Ok(())
}
}
#[derive(Debug, Serialize, Clone)]
#[serde(rename_all = "camelCase")]
pub struct InterfacePropertyDef {
@ -37,6 +56,44 @@ pub struct InterfacePropertyDef {
pub type_params: Vec<TsTypeParamDef>,
}
impl Display for InterfacePropertyDef {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
write!(
f,
"{}{}",
colors::bold(&self.name),
display_optional(self.optional),
)?;
if let Some(ts_type) = &self.ts_type {
write!(f, ": {}", ts_type)?;
}
Ok(())
}
}
#[derive(Debug, Serialize, Clone)]
#[serde(rename_all = "camelCase")]
pub struct InterfaceIndexSignatureDef {
pub readonly: bool,
pub params: Vec<ParamDef>,
pub ts_type: Option<TsTypeDef>,
}
impl Display for InterfaceIndexSignatureDef {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
write!(
f,
"{}[{}]",
display_readonly(self.readonly),
SliceDisplayer::new(&self.params, ", ", false)
)?;
if let Some(ts_type) = &self.ts_type {
write!(f, ": {}", ts_type)?;
}
Ok(())
}
}
#[derive(Debug, Serialize, Clone)]
#[serde(rename_all = "camelCase")]
pub struct InterfaceCallSignatureDef {
@ -50,10 +107,11 @@ pub struct InterfaceCallSignatureDef {
#[derive(Debug, Serialize, Clone)]
#[serde(rename_all = "camelCase")]
pub struct InterfaceDef {
pub extends: Vec<String>,
pub extends: Vec<TsTypeDef>,
pub methods: Vec<InterfaceMethodDef>,
pub properties: Vec<InterfacePropertyDef>,
pub call_signatures: Vec<InterfaceCallSignatureDef>,
pub index_signatures: Vec<InterfaceIndexSignatureDef>,
pub type_params: Vec<TsTypeParamDef>,
}
@ -84,6 +142,7 @@ pub fn get_doc_for_ts_interface_decl(
let mut methods = vec![];
let mut properties = vec![];
let mut call_signatures = vec![];
let mut index_signatures = vec![];
for type_element in &interface_decl.body.body {
use crate::swc_ecma_ast::TsTypeElement::*;
@ -95,7 +154,10 @@ pub fn get_doc_for_ts_interface_decl(
let mut params = vec![];
for param in &ts_method_sig.params {
let param_def = ts_fn_param_to_param_def(param);
let param_def = ts_fn_param_to_param_def(
param,
Some(&doc_parser.ast_parser.source_map),
);
params.push(param_def);
}
@ -131,7 +193,10 @@ pub fn get_doc_for_ts_interface_decl(
let mut params = vec![];
for param in &ts_prop_sig.params {
let param_def = ts_fn_param_to_param_def(param);
let param_def = ts_fn_param_to_param_def(
param,
Some(&doc_parser.ast_parser.source_map),
);
params.push(param_def);
}
@ -164,7 +229,10 @@ pub fn get_doc_for_ts_interface_decl(
let mut params = vec![];
for param in &ts_call_sig.params {
let param_def = ts_fn_param_to_param_def(param);
let param_def = ts_fn_param_to_param_def(
param,
Some(&doc_parser.ast_parser.source_map),
);
params.push(param_def);
}
@ -189,9 +257,27 @@ pub fn get_doc_for_ts_interface_decl(
};
call_signatures.push(call_sig_def);
}
TsIndexSignature(ts_index_sig) => {
let mut params = vec![];
for param in &ts_index_sig.params {
let param_def = ts_fn_param_to_param_def(param, None);
params.push(param_def);
}
let ts_type = ts_index_sig
.type_ann
.as_ref()
.map(|rt| (&*rt.type_ann).into());
let index_sig_def = InterfaceIndexSignatureDef {
readonly: ts_index_sig.readonly,
params,
ts_type,
};
index_signatures.push(index_sig_def);
}
// TODO:
TsConstructSignatureDecl(_) => {}
TsIndexSignature(_) => {}
}
}
@ -199,17 +285,18 @@ pub fn get_doc_for_ts_interface_decl(
interface_decl.type_params.as_ref(),
);
let extends: Vec<String> = interface_decl
let extends = interface_decl
.extends
.iter()
.map(|expr| ts_entity_name_to_name(&expr.expr))
.collect();
.map(|expr| expr.into())
.collect::<Vec<TsTypeDef>>();
let interface_def = InterfaceDef {
extends,
methods,
properties,
call_signatures,
index_signatures,
type_params,
};

View file

@ -1,5 +1,6 @@
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
pub mod class;
mod display;
pub mod r#enum;
pub mod function;
pub mod interface;
@ -17,9 +18,9 @@ pub mod variable;
pub use node::DocNode;
pub use node::DocNodeKind;
pub use node::Location;
pub use node::ParamDef;
pub use node::ParamKind;
pub use params::ParamDef;
pub use parser::DocParser;
pub use printer::DocPrinter;
#[cfg(test)]
mod tests;

View file

@ -35,7 +35,8 @@ pub fn get_doc_node_for_export_decl(
}
}
Decl::Fn(fn_decl) => {
let (name, function_def) = super::function::get_doc_for_fn_decl(fn_decl);
let (name, function_def) =
super::function::get_doc_for_fn_decl(doc_parser, fn_decl);
DocNode {
kind: DocNodeKind::Function,
name,

View file

@ -14,24 +14,6 @@ pub enum DocNodeKind {
Namespace,
}
#[derive(Debug, Serialize, Clone)]
#[serde(rename_all = "camelCase")]
pub enum ParamKind {
Identifier,
Rest,
Array,
Object,
}
#[derive(Debug, Serialize, Clone)]
#[serde(rename_all = "camelCase")]
pub struct ParamDef {
pub name: String,
pub kind: ParamKind,
pub optional: bool,
pub ts_type: Option<super::ts_type::TsTypeDef>,
}
#[derive(Debug, Serialize, Clone, PartialEq)]
pub struct Location {
pub filename: String,
@ -82,7 +64,7 @@ pub struct Reexport {
#[derive(Debug, Serialize, Clone)]
#[serde(rename_all = "camelCase")]
pub struct ModuleDoc {
pub exports: Vec<DocNode>,
pub definitions: Vec<DocNode>,
pub reexports: Vec<Reexport>,
}

View file

@ -1,58 +1,228 @@
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
use super::display::{display_optional, SliceDisplayer};
use super::ts_type::{ts_type_ann_to_def, TsTypeDef};
use crate::swc_common::SourceMap;
use crate::swc_ecma_ast;
use crate::swc_ecma_ast::{ObjectPatProp, Pat, TsFnParam};
use serde::Serialize;
use std::fmt::{Display, Formatter, Result as FmtResult};
use super::ts_type::ts_type_ann_to_def;
use super::ParamDef;
use super::ParamKind;
use crate::swc_ecma_ast::Pat;
use crate::swc_ecma_ast::TsFnParam;
#[derive(Debug, Serialize, Clone)]
#[serde(rename_all = "camelCase")]
#[serde(tag = "kind")]
pub enum ParamDef {
#[serde(rename_all = "camelCase")]
Array {
elements: Vec<Option<ParamDef>>,
optional: bool,
ts_type: Option<TsTypeDef>,
},
#[serde(rename_all = "camelCase")]
Assign {
left: Box<ParamDef>,
right: String,
ts_type: Option<TsTypeDef>,
},
#[serde(rename_all = "camelCase")]
Identifier {
name: String,
optional: bool,
ts_type: Option<TsTypeDef>,
},
#[serde(rename_all = "camelCase")]
Object {
props: Vec<ObjectPatPropDef>,
optional: bool,
ts_type: Option<TsTypeDef>,
},
#[serde(rename_all = "camelCase")]
Rest {
arg: Box<ParamDef>,
ts_type: Option<TsTypeDef>,
},
}
pub fn ident_to_param_def(ident: &swc_ecma_ast::Ident) -> ParamDef {
impl Display for ParamDef {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
match self {
Self::Array {
elements,
optional,
ts_type,
} => {
write!(f, "[")?;
if !elements.is_empty() {
if let Some(v) = &elements[0] {
write!(f, "{}", v)?;
}
for maybe_v in &elements[1..] {
write!(f, ", ")?;
if let Some(v) = maybe_v {
write!(f, "{}", v)?;
}
}
}
write!(f, "]")?;
write!(f, "{}", display_optional(*optional))?;
if let Some(ts_type) = ts_type {
write!(f, ": {}", ts_type)?;
}
Ok(())
}
Self::Assign { left, ts_type, .. } => {
write!(f, "{}", left)?;
if let Some(ts_type) = ts_type {
write!(f, ": {}", ts_type)?;
}
// TODO(SyrupThinker) As we cannot display expressions the value is just omitted
// write!(f, " = {}", right)?;
Ok(())
}
Self::Identifier {
name,
optional,
ts_type,
} => {
write!(f, "{}{}", name, display_optional(*optional))?;
if let Some(ts_type) = ts_type {
write!(f, ": {}", ts_type)?;
}
Ok(())
}
Self::Object {
props,
optional,
ts_type,
} => {
write!(
f,
"{{{}}}{}",
SliceDisplayer::new(&props, ", ", false),
display_optional(*optional)
)?;
if let Some(ts_type) = ts_type {
write!(f, ": {}", ts_type)?;
}
Ok(())
}
Self::Rest { arg, ts_type } => {
write!(f, "...{}", arg)?;
if let Some(ts_type) = ts_type {
write!(f, ": {}", ts_type)?;
}
Ok(())
}
}
}
}
#[derive(Debug, Serialize, Clone)]
#[serde(rename_all = "camelCase")]
#[serde(tag = "kind")]
pub enum ObjectPatPropDef {
Assign { key: String, value: Option<String> },
KeyValue { key: String, value: Box<ParamDef> },
Rest { arg: Box<ParamDef> },
}
impl Display for ObjectPatPropDef {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
match self {
Self::KeyValue { key, .. } => {
// The internal identifier does not need to be exposed
write!(f, "{}", key)
}
Self::Assign { key, value } => {
if let Some(_value) = value {
// TODO(SyrupThinker) As we cannot display expressions the value is just omitted
write!(f, "{}", key)
} else {
write!(f, "{}", key)
}
}
Self::Rest { arg } => write!(f, "...{}", arg),
}
}
}
pub fn ident_to_param_def(
ident: &swc_ecma_ast::Ident,
_source_map: Option<&SourceMap>,
) -> ParamDef {
let ts_type = ident.type_ann.as_ref().map(|rt| ts_type_ann_to_def(rt));
ParamDef {
ParamDef::Identifier {
name: ident.sym.to_string(),
kind: ParamKind::Identifier,
optional: ident.optional,
ts_type,
}
}
fn rest_pat_to_param_def(rest_pat: &swc_ecma_ast::RestPat) -> ParamDef {
let name = match &*rest_pat.arg {
Pat::Ident(ident) => ident.sym.to_string(),
_ => "<TODO>".to_string(),
};
fn rest_pat_to_param_def(
rest_pat: &swc_ecma_ast::RestPat,
source_map: Option<&SourceMap>,
) -> ParamDef {
let ts_type = rest_pat.type_ann.as_ref().map(|rt| ts_type_ann_to_def(rt));
ParamDef {
name,
kind: ParamKind::Rest,
optional: false,
ParamDef::Rest {
arg: Box::new(pat_to_param_def(&*rest_pat.arg, source_map)),
ts_type,
}
}
fn object_pat_to_param_def(object_pat: &swc_ecma_ast::ObjectPat) -> ParamDef {
fn object_pat_prop_to_def(
object_pat_prop: &ObjectPatProp,
source_map: Option<&SourceMap>,
) -> ObjectPatPropDef {
match object_pat_prop {
ObjectPatProp::Assign(assign) => ObjectPatPropDef::Assign {
key: assign.key.sym.to_string(),
value: assign.value.as_ref().map(|_| "<UNIMPLEMENTED>".to_string()),
},
ObjectPatProp::KeyValue(keyvalue) => ObjectPatPropDef::KeyValue {
key: prop_name_to_string(&keyvalue.key, source_map),
value: Box::new(pat_to_param_def(&*keyvalue.value, source_map)),
},
ObjectPatProp::Rest(rest) => ObjectPatPropDef::Rest {
arg: Box::new(pat_to_param_def(&*rest.arg, source_map)),
},
}
}
fn object_pat_to_param_def(
object_pat: &swc_ecma_ast::ObjectPat,
source_map: Option<&SourceMap>,
) -> ParamDef {
let props = object_pat
.props
.iter()
.map(|prop| object_pat_prop_to_def(prop, source_map))
.collect::<Vec<_>>();
let ts_type = object_pat
.type_ann
.as_ref()
.map(|rt| ts_type_ann_to_def(rt));
ParamDef {
name: "".to_string(),
kind: ParamKind::Object,
ParamDef::Object {
props,
optional: object_pat.optional,
ts_type,
}
}
fn array_pat_to_param_def(array_pat: &swc_ecma_ast::ArrayPat) -> ParamDef {
fn array_pat_to_param_def(
array_pat: &swc_ecma_ast::ArrayPat,
source_map: Option<&SourceMap>,
) -> ParamDef {
let elements = array_pat
.elems
.iter()
.map(|elem| elem.as_ref().map(|e| pat_to_param_def(e, source_map)))
.collect::<Vec<Option<_>>>();
let ts_type = array_pat.type_ann.as_ref().map(|rt| ts_type_ann_to_def(rt));
ParamDef {
name: "".to_string(),
kind: ParamKind::Array,
ParamDef::Array {
elements,
optional: array_pat.optional,
ts_type,
}
@ -60,28 +230,61 @@ fn array_pat_to_param_def(array_pat: &swc_ecma_ast::ArrayPat) -> ParamDef {
pub fn assign_pat_to_param_def(
assign_pat: &swc_ecma_ast::AssignPat,
source_map: Option<&SourceMap>,
) -> ParamDef {
pat_to_param_def(&*assign_pat.left)
let ts_type = assign_pat
.type_ann
.as_ref()
.map(|rt| ts_type_ann_to_def(rt));
ParamDef::Assign {
left: Box::new(pat_to_param_def(&*assign_pat.left, source_map)),
right: "<UNIMPLEMENTED>".to_string(),
ts_type,
}
}
pub fn pat_to_param_def(pat: &swc_ecma_ast::Pat) -> ParamDef {
pub fn pat_to_param_def(
pat: &swc_ecma_ast::Pat,
source_map: Option<&SourceMap>,
) -> ParamDef {
match pat {
Pat::Ident(ident) => ident_to_param_def(ident),
Pat::Array(array_pat) => array_pat_to_param_def(array_pat),
Pat::Rest(rest_pat) => rest_pat_to_param_def(rest_pat),
Pat::Object(object_pat) => object_pat_to_param_def(object_pat),
Pat::Assign(assign_pat) => assign_pat_to_param_def(assign_pat),
Pat::Ident(ident) => ident_to_param_def(ident, source_map),
Pat::Array(array_pat) => array_pat_to_param_def(array_pat, source_map),
Pat::Rest(rest_pat) => rest_pat_to_param_def(rest_pat, source_map),
Pat::Object(object_pat) => object_pat_to_param_def(object_pat, source_map),
Pat::Assign(assign_pat) => assign_pat_to_param_def(assign_pat, source_map),
_ => unreachable!(),
}
}
pub fn ts_fn_param_to_param_def(
ts_fn_param: &swc_ecma_ast::TsFnParam,
source_map: Option<&SourceMap>,
) -> ParamDef {
match ts_fn_param {
TsFnParam::Ident(ident) => ident_to_param_def(ident),
TsFnParam::Array(array_pat) => array_pat_to_param_def(array_pat),
TsFnParam::Rest(rest_pat) => rest_pat_to_param_def(rest_pat),
TsFnParam::Object(object_pat) => object_pat_to_param_def(object_pat),
TsFnParam::Ident(ident) => ident_to_param_def(ident, source_map),
TsFnParam::Array(array_pat) => {
array_pat_to_param_def(array_pat, source_map)
}
TsFnParam::Rest(rest_pat) => rest_pat_to_param_def(rest_pat, source_map),
TsFnParam::Object(object_pat) => {
object_pat_to_param_def(object_pat, source_map)
}
}
}
pub fn prop_name_to_string(
prop_name: &swc_ecma_ast::PropName,
source_map: Option<&SourceMap>,
) -> String {
use crate::swc_ecma_ast::PropName;
match prop_name {
PropName::Ident(ident) => ident.sym.to_string(),
PropName::Str(str_) => str_.value.to_string(),
PropName::Num(num) => num.value.to_string(),
PropName::Computed(comp_prop_name) => source_map
.map(|sm| sm.span_to_snippet(comp_prop_name.span).unwrap())
.unwrap_or_else(|| "<UNAVAILABLE>".to_string()),
}
}

View file

@ -44,13 +44,15 @@ pub trait DocFileLoader {
pub struct DocParser {
pub ast_parser: AstParser,
pub loader: Box<dyn DocFileLoader>,
pub private: bool,
}
impl DocParser {
pub fn new(loader: Box<dyn DocFileLoader>) -> Self {
pub fn new(loader: Box<dyn DocFileLoader>, private: bool) -> Self {
DocParser {
loader,
ast_parser: AstParser::new(),
private,
}
}
@ -70,7 +72,7 @@ impl DocParser {
self.get_doc_nodes_for_module_body(module.body.clone());
let reexports = self.get_reexports_for_module_body(module.body);
let module_doc = ModuleDoc {
exports: doc_entries,
definitions: doc_entries,
reexports,
};
Ok(module_doc)
@ -90,7 +92,7 @@ impl DocParser {
source_code: &str,
) -> Result<Vec<DocNode>, ErrBox> {
let module_doc = self.parse_module(file_name, &source_code)?;
Ok(module_doc.exports)
Ok(module_doc.definitions)
}
async fn flatten_reexports(
@ -187,10 +189,10 @@ impl DocParser {
let mut flattenned_reexports = self
.flatten_reexports(&module_doc.reexports, file_name)
.await?;
flattenned_reexports.extend(module_doc.exports);
flattenned_reexports.extend(module_doc.definitions);
flattenned_reexports
} else {
module_doc.exports
module_doc.definitions
};
Ok(flattened_docs)
@ -231,8 +233,10 @@ impl DocParser {
}
}
DefaultDecl::Fn(fn_expr) => {
let function_def =
crate::doc::function::function_to_function_def(&fn_expr.function);
let function_def = crate::doc::function::function_to_function_def(
self,
&fn_expr.function,
);
DocNode {
kind: DocNodeKind::Function,
name,
@ -292,7 +296,7 @@ impl DocParser {
pub fn get_doc_node_for_decl(&self, decl: &Decl) -> Option<DocNode> {
match decl {
Decl::Class(class_decl) => {
if !class_decl.declare {
if !self.private && !class_decl.declare {
return None;
}
let (name, class_def) =
@ -313,11 +317,11 @@ impl DocParser {
})
}
Decl::Fn(fn_decl) => {
if !fn_decl.declare {
if !self.private && !fn_decl.declare {
return None;
}
let (name, function_def) =
super::function::get_doc_for_fn_decl(fn_decl);
super::function::get_doc_for_fn_decl(self, fn_decl);
let (js_doc, location) = self.details_for_span(fn_decl.function.span);
Some(DocNode {
kind: DocNodeKind::Function,
@ -334,7 +338,7 @@ impl DocParser {
})
}
Decl::Var(var_decl) => {
if !var_decl.declare {
if !self.private && !var_decl.declare {
return None;
}
let (name, var_def) = super::variable::get_doc_for_var_decl(var_decl);
@ -354,7 +358,7 @@ impl DocParser {
})
}
Decl::TsInterface(ts_interface_decl) => {
if !ts_interface_decl.declare {
if !self.private && !ts_interface_decl.declare {
return None;
}
let (name, interface_def) =
@ -378,7 +382,7 @@ impl DocParser {
})
}
Decl::TsTypeAlias(ts_type_alias) => {
if !ts_type_alias.declare {
if !self.private && !ts_type_alias.declare {
return None;
}
let (name, type_alias_def) =
@ -402,7 +406,7 @@ impl DocParser {
})
}
Decl::TsEnum(ts_enum) => {
if !ts_enum.declare {
if !self.private && !ts_enum.declare {
return None;
}
let (name, enum_def) =
@ -423,7 +427,7 @@ impl DocParser {
})
}
Decl::TsModule(ts_module) => {
if !ts_module.declare {
if !self.private && !ts_module.declare {
return None;
}
let (name, namespace_def) =

View file

@ -12,540 +12,463 @@
use crate::colors;
use crate::doc;
use crate::doc::ts_type::TsTypeDefKind;
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 fn format(doc_nodes: Vec<doc::DocNode>) -> String {
format_(doc_nodes, 0)
pub struct DocPrinter<'a> {
doc_nodes: &'a [doc::DocNode],
details: bool,
private: bool,
}
pub fn format_details(node: doc::DocNode) -> String {
let mut details = String::new();
details.push_str(&format!(
"{}",
colors::gray(&format!(
"Defined in {}:{}:{} \n\n",
node.location.filename, node.location.line, node.location.col
))
));
details.push_str(&format_signature(&node, 0));
let js_doc = node.js_doc.clone();
if let Some(js_doc) = js_doc {
details.push_str(&format_jsdoc(js_doc, 1));
}
details.push_str("\n");
let maybe_extra = match node.kind {
DocNodeKind::Class => Some(format_class_details(node)),
DocNodeKind::Enum => Some(format_enum_details(node)),
DocNodeKind::Namespace => Some(format_namespace_details(node)),
_ => None,
};
if let Some(extra) = maybe_extra {
details.push_str(&extra);
impl<'a> DocPrinter<'a> {
pub fn new(
doc_nodes: &[doc::DocNode],
details: bool,
private: bool,
) -> DocPrinter {
DocPrinter {
doc_nodes,
details,
private,
}
}
details
}
fn kind_order(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(node: &doc::DocNode, indent: i64) -> String {
match node.kind {
DocNodeKind::Function => format_function_signature(&node, indent),
DocNodeKind::Variable => format_variable_signature(&node, indent),
DocNodeKind::Class => format_class_signature(&node, indent),
DocNodeKind::Enum => format_enum_signature(&node, indent),
DocNodeKind::Interface => format_interface_signature(&node, indent),
DocNodeKind::TypeAlias => format_type_alias_signature(&node, indent),
DocNodeKind::Namespace => format_namespace_signature(&node, indent),
}
}
fn format_(doc_nodes: Vec<doc::DocNode>, indent: i64) -> String {
let mut sorted = doc_nodes;
sorted.sort_unstable_by(|a, b| {
let kind_cmp = kind_order(&a.kind).cmp(&kind_order(&b.kind));
if kind_cmp == core::cmp::Ordering::Equal {
a.name.cmp(&b.name)
pub fn format(&self, w: &mut Formatter<'_>) -> FmtResult {
if self.details {
self.format_details(w, self.doc_nodes, 0)
} else {
kind_cmp
}
});
let mut output = String::new();
for node in sorted {
output.push_str(&format_signature(&node, indent));
if let Some(js_doc) = node.js_doc {
output.push_str(&format_jsdoc(js_doc, indent));
}
output.push_str("\n");
if DocNodeKind::Namespace == node.kind {
output.push_str(&format_(
node.namespace_def.as_ref().unwrap().elements.clone(),
indent + 1,
));
output.push_str("\n");
};
}
output
}
fn render_params(params: Vec<doc::ParamDef>) -> String {
let mut rendered = String::from("");
if !params.is_empty() {
for param in params {
rendered += param.name.as_str();
if param.optional {
rendered += "?";
}
if let Some(ts_type) = param.ts_type {
rendered += ": ";
rendered += render_ts_type(ts_type).as_str();
}
rendered += ", ";
}
rendered.truncate(rendered.len() - 2);
}
rendered
}
fn render_ts_type(ts_type: doc::ts_type::TsTypeDef) -> String {
if ts_type.kind.is_none() {
return "<UNIMPLEMENTED>".to_string();
}
let kind = ts_type.kind.unwrap();
match kind {
TsTypeDefKind::Array => {
format!("{}[]", render_ts_type(*ts_type.array.unwrap()))
}
TsTypeDefKind::Conditional => {
let conditional = ts_type.conditional_type.unwrap();
format!(
"{} extends {} ? {} : {}",
render_ts_type(*conditional.check_type),
render_ts_type(*conditional.extends_type),
render_ts_type(*conditional.true_type),
render_ts_type(*conditional.false_type)
)
}
TsTypeDefKind::FnOrConstructor => {
let fn_or_constructor = ts_type.fn_or_constructor.unwrap();
format!(
"{}({}) => {}",
if fn_or_constructor.constructor {
"new "
} else {
""
},
render_params(fn_or_constructor.params),
render_ts_type(fn_or_constructor.ts_type),
)
}
TsTypeDefKind::IndexedAccess => {
let indexed_access = ts_type.indexed_access.unwrap();
format!(
"{}[{}]",
render_ts_type(*indexed_access.obj_type),
render_ts_type(*indexed_access.index_type)
)
}
TsTypeDefKind::Intersection => {
let intersection = ts_type.intersection.unwrap();
let mut output = "".to_string();
if !intersection.is_empty() {
for ts_type in intersection {
output += render_ts_type(ts_type).as_str();
output += " & "
}
output.truncate(output.len() - 3);
}
output
}
TsTypeDefKind::Keyword => ts_type.keyword.unwrap(),
TsTypeDefKind::Literal => {
let literal = ts_type.literal.unwrap();
match literal.kind {
doc::ts_type::LiteralDefKind::Boolean => {
format!("{}", literal.boolean.unwrap())
}
doc::ts_type::LiteralDefKind::String => {
"\"".to_string() + literal.string.unwrap().as_str() + "\""
}
doc::ts_type::LiteralDefKind::Number => {
format!("{}", literal.number.unwrap())
}
}
}
TsTypeDefKind::Optional => {
format!("{}?", render_ts_type(*ts_type.optional.unwrap()))
}
TsTypeDefKind::Parenthesized => {
format!("({})", render_ts_type(*ts_type.parenthesized.unwrap()))
}
TsTypeDefKind::Rest => {
format!("...{}", render_ts_type(*ts_type.rest.unwrap()))
}
TsTypeDefKind::This => "this".to_string(),
TsTypeDefKind::Tuple => {
let tuple = ts_type.tuple.unwrap();
let mut output = "[".to_string();
if !tuple.is_empty() {
for ts_type in tuple {
output += render_ts_type(ts_type).as_str();
output += ", "
}
output.truncate(output.len() - 2);
}
output += "]";
output
}
TsTypeDefKind::TypeLiteral => {
let mut output = "".to_string();
let type_literal = ts_type.type_literal.unwrap();
for node in type_literal.call_signatures {
output += format!(
"({}){}, ",
render_params(node.params),
if let Some(ts_type) = node.ts_type {
format!(": {}", render_ts_type(ts_type))
} else {
"".to_string()
}
)
.as_str()
}
for node in type_literal.methods {
output += format!(
"{}({}){}, ",
node.name,
render_params(node.params),
if let Some(return_type) = node.return_type {
format!(": {}", render_ts_type(return_type))
} else {
"".to_string()
}
)
.as_str()
}
for node in type_literal.properties {
output += format!(
"{}{}, ",
node.name,
if let Some(ts_type) = node.ts_type {
format!(": {}", render_ts_type(ts_type))
} else {
"".to_string()
}
)
.as_str()
}
if !output.is_empty() {
output.truncate(output.len() - 2);
}
"{ ".to_string() + output.as_str() + " }"
}
TsTypeDefKind::TypeOperator => {
let operator = ts_type.type_operator.unwrap();
format!("{} {}", operator.operator, render_ts_type(operator.ts_type))
}
TsTypeDefKind::TypeQuery => {
format!("typeof {}", ts_type.type_query.unwrap())
}
TsTypeDefKind::TypeRef => {
let type_ref = ts_type.type_ref.unwrap();
let mut final_output = type_ref.type_name;
if let Some(type_params) = type_ref.type_params {
let mut output = "".to_string();
if !type_params.is_empty() {
for ts_type in type_params {
output += render_ts_type(ts_type).as_str();
output += ", "
}
output.truncate(output.len() - 2);
}
final_output += format!("<{}>", output).as_str();
}
final_output
}
TsTypeDefKind::Union => {
let union = ts_type.union.unwrap();
let mut output = "".to_string();
if !union.is_empty() {
for ts_type in union {
output += render_ts_type(ts_type).as_str();
output += " | "
}
output.truncate(output.len() - 3);
}
output
self.format_summary(w, self.doc_nodes, 0)
}
}
}
fn add_indent(string: String, indent: i64) -> String {
let mut indent_str = String::new();
for _ in 0..(indent * 2) {
indent_str += " ";
}
indent_str += string.as_str();
indent_str
}
// TODO: this should use some sort of markdown to console parser.
fn format_jsdoc(jsdoc: String, indent: i64) -> String {
let lines = jsdoc.split("\n\n").map(|line| line.replace("\n", " "));
let mut js_doc = String::new();
for line in lines {
js_doc.push_str(&add_indent(format!("{}\n", line), indent + 1));
}
format!("{}", colors::gray(&js_doc))
}
fn format_class_details(node: doc::DocNode) -> String {
let mut details = String::new();
let class_def = node.class_def.unwrap();
for node in class_def.constructors {
details.push_str(&add_indent(
format!(
"{} {}({})\n",
colors::magenta("constructor"),
colors::bold(&node.name),
render_params(node.params),
),
1,
));
}
for node in class_def.properties.iter().filter(|node| {
node
.accessibility
.unwrap_or(swc_ecma_ast::Accessibility::Public)
!= swc_ecma_ast::Accessibility::Private
}) {
details.push_str(&add_indent(
format!(
"{}{}{}{}\n",
colors::magenta(
match node
.accessibility
.unwrap_or(swc_ecma_ast::Accessibility::Public)
{
swc_ecma_ast::Accessibility::Protected => "protected ",
_ => "",
}
),
colors::bold(&node.name),
if node.optional {
"?".to_string()
} else {
"".to_string()
},
if let Some(ts_type) = node.ts_type.clone() {
format!(": {}", render_ts_type(ts_type))
} else {
"".to_string()
}
),
1,
));
}
for node in class_def.methods.iter().filter(|node| {
node
.accessibility
.unwrap_or(swc_ecma_ast::Accessibility::Public)
!= swc_ecma_ast::Accessibility::Private
}) {
let function_def = node.function_def.clone();
details.push_str(&add_indent(
format!(
"{}{}{}{}({}){}\n",
colors::magenta(
match node
.accessibility
.unwrap_or(swc_ecma_ast::Accessibility::Public)
{
swc_ecma_ast::Accessibility::Protected => "protected ",
_ => "",
}
),
colors::magenta(match node.kind {
swc_ecma_ast::MethodKind::Getter => "get ",
swc_ecma_ast::MethodKind::Setter => "set ",
_ => "",
}),
colors::bold(&node.name),
if node.optional { "?" } else { "" },
render_params(function_def.params),
if let Some(return_type) = function_def.return_type {
format!(": {}", render_ts_type(return_type))
} else {
"".to_string()
}
),
1,
));
}
details.push_str("\n");
details
}
fn format_enum_details(node: doc::DocNode) -> String {
let mut details = String::new();
let enum_def = node.enum_def.unwrap();
for member in enum_def.members {
details
.push_str(&add_indent(format!("{}\n", colors::bold(&member.name)), 1));
}
details.push_str("\n");
details
}
fn format_namespace_details(node: doc::DocNode) -> String {
let mut ns = String::new();
let elements = node.namespace_def.unwrap().elements;
for node in elements {
ns.push_str(&format_signature(&node, 1));
}
ns.push_str("\n");
ns
}
fn format_function_signature(node: &doc::DocNode, indent: i64) -> String {
let function_def = node.function_def.clone().unwrap();
add_indent(
format!(
"{} {}({}){}\n",
colors::magenta("function"),
colors::bold(&node.name),
render_params(function_def.params),
if let Some(return_type) = function_def.return_type {
format!(": {}", render_ts_type(return_type).as_str())
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 {
"".to_string()
kind_cmp
}
),
indent,
)
}
});
fn format_class_signature(node: &doc::DocNode, indent: i64) -> String {
let class_def = node.class_def.clone().unwrap();
let extends_suffix = if let Some(extends) = class_def.extends {
format!(" {} {}", colors::magenta("extends"), colors::bold(&extends))
} else {
String::from("")
};
for node in sorted {
self.format_signature(w, &node, indent)?;
let implements = &class_def.implements;
let implements_suffix = if !implements.is_empty() {
format!(
" {} {}",
colors::magenta("implements"),
colors::bold(&implements.join(", "))
)
} else {
String::from("")
};
if let Some(js_doc) = &node.js_doc {
self.format_jsdoc(w, js_doc, indent + 1, self.details)?;
}
add_indent(
format!(
"{} {}{}{}\n",
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),
extends_suffix,
implements_suffix,
),
indent,
)
}
)?;
if !class_def.type_params.is_empty() {
write!(
w,
"<{}>",
SliceDisplayer::new(&class_def.type_params, ", ", false)
)?;
}
fn format_variable_signature(node: &doc::DocNode, indent: i64) -> String {
let variable_def = node.variable_def.clone().unwrap();
add_indent(
format!(
"{} {}{}\n",
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 {
format!(": {}", render_ts_type(ts_type))
} else {
"".to_string()
}
),
indent,
)
)?;
if let Some(ts_type) = &variable_def.ts_type {
write!(w, ": {}", ts_type)?;
}
writeln!(w)
}
}
fn format_enum_signature(node: &doc::DocNode, indent: i64) -> String {
add_indent(
format!("{} {}\n", colors::magenta("enum"), colors::bold(&node.name)),
indent,
)
}
fn format_interface_signature(node: &doc::DocNode, indent: i64) -> String {
let interface_def = node.interface_def.clone().unwrap();
let extends = &interface_def.extends;
let extends_suffix = if !extends.is_empty() {
format!(
" {} {}",
colors::magenta("extends"),
colors::bold(&extends.join(", "))
)
} else {
String::from("")
};
add_indent(
format!(
"{} {}{}\n",
colors::magenta("interface"),
colors::bold(&node.name),
extends_suffix
),
indent,
)
}
fn format_type_alias_signature(node: &doc::DocNode, indent: i64) -> String {
add_indent(
format!("{} {}\n", colors::magenta("type"), colors::bold(&node.name)),
indent,
)
}
fn format_namespace_signature(node: &doc::DocNode, indent: i64) -> String {
add_indent(
format!(
"{} {}\n",
colors::magenta("namespace"),
colors::bold(&node.name)
),
indent,
)
impl<'a> Display for DocPrinter<'a> {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
self.format(f)
}
}

File diff suppressed because it is too large Load diff

View file

@ -1,30 +1,23 @@
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
use super::display::{display_readonly, SliceDisplayer};
use super::interface::expr_to_name;
use super::params::ts_fn_param_to_param_def;
use super::ts_type_param::maybe_type_param_decl_to_type_param_defs;
use super::ts_type_param::TsTypeParamDef;
use super::ParamDef;
use crate::colors;
use crate::doc;
use crate::swc_ecma_ast;
use crate::swc_ecma_ast::TsArrayType;
use crate::swc_ecma_ast::TsConditionalType;
use crate::swc_ecma_ast::TsFnOrConstructorType;
use crate::swc_ecma_ast::TsIndexedAccessType;
use crate::swc_ecma_ast::TsKeywordType;
use crate::swc_ecma_ast::TsLit;
use crate::swc_ecma_ast::TsLitType;
use crate::swc_ecma_ast::TsOptionalType;
use crate::swc_ecma_ast::TsParenthesizedType;
use crate::swc_ecma_ast::TsRestType;
use crate::swc_ecma_ast::TsThisType;
use crate::swc_ecma_ast::TsTupleType;
use crate::swc_ecma_ast::TsType;
use crate::swc_ecma_ast::TsTypeAnn;
use crate::swc_ecma_ast::TsTypeLit;
use crate::swc_ecma_ast::TsTypeOperator;
use crate::swc_ecma_ast::TsTypeQuery;
use crate::swc_ecma_ast::TsTypeRef;
use crate::swc_ecma_ast::TsUnionOrIntersectionType;
use crate::swc_ecma_ast::{
TsArrayType, TsConditionalType, TsExprWithTypeArgs, TsFnOrConstructorType,
TsIndexedAccessType, TsKeywordType, TsLit, TsLitType, TsOptionalType,
TsParenthesizedType, TsRestType, TsThisType, TsTupleType, TsType, TsTypeAnn,
TsTypeLit, TsTypeOperator, TsTypeParamInstantiation, TsTypeQuery, TsTypeRef,
TsUnionOrIntersectionType,
};
use serde::Serialize;
use std::fmt::{Display, Formatter, Result as FmtResult};
// pub enum TsType {
// * TsKeywordType(TsKeywordType),
// * TsThisType(TsThisType),
@ -316,7 +309,37 @@ impl Into<TsTypeDef> for &TsTypeRef {
};
TsTypeDef {
repr: type_name.to_string(),
repr: type_name.clone(),
type_ref: Some(TsTypeRefDef {
type_name,
type_params,
}),
kind: Some(TsTypeDefKind::TypeRef),
..Default::default()
}
}
}
impl Into<TsTypeDef> for &TsExprWithTypeArgs {
fn into(self) -> TsTypeDef {
let type_name = ts_entity_name_to_name(&self.expr);
let type_params = if let Some(type_params_inst) = &self.type_args {
let mut ts_type_defs = vec![];
for type_box in &type_params_inst.params {
let ts_type: &TsType = &(*type_box);
let def: TsTypeDef = ts_type.into();
ts_type_defs.push(def);
}
Some(ts_type_defs)
} else {
None
};
TsTypeDef {
repr: type_name.clone(),
type_ref: Some(TsTypeRefDef {
type_name,
type_params,
@ -348,6 +371,7 @@ impl Into<TsTypeDef> for &TsTypeLit {
let mut methods = vec![];
let mut properties = vec![];
let mut call_signatures = vec![];
let mut index_signatures = vec![];
for type_element in &self.members {
use crate::swc_ecma_ast::TsTypeElement::*;
@ -357,7 +381,7 @@ impl Into<TsTypeDef> for &TsTypeLit {
let mut params = vec![];
for param in &ts_method_sig.params {
let param_def = ts_fn_param_to_param_def(param);
let param_def = ts_fn_param_to_param_def(param, None);
params.push(param_def);
}
@ -384,7 +408,7 @@ impl Into<TsTypeDef> for &TsTypeLit {
let mut params = vec![];
for param in &ts_prop_sig.params {
let param_def = ts_fn_param_to_param_def(param);
let param_def = ts_fn_param_to_param_def(param, None);
params.push(param_def);
}
@ -409,7 +433,7 @@ impl Into<TsTypeDef> for &TsTypeLit {
TsCallSignatureDecl(ts_call_sig) => {
let mut params = vec![];
for param in &ts_call_sig.params {
let param_def = ts_fn_param_to_param_def(param);
let param_def = ts_fn_param_to_param_def(param, None);
params.push(param_def);
}
@ -429,9 +453,27 @@ impl Into<TsTypeDef> for &TsTypeLit {
};
call_signatures.push(call_sig_def);
}
TsIndexSignature(ts_index_sig) => {
let mut params = vec![];
for param in &ts_index_sig.params {
let param_def = ts_fn_param_to_param_def(param, None);
params.push(param_def);
}
let ts_type = ts_index_sig
.type_ann
.as_ref()
.map(|rt| (&*rt.type_ann).into());
let index_sig_def = LiteralIndexSignatureDef {
readonly: ts_index_sig.readonly,
params,
ts_type,
};
index_signatures.push(index_sig_def);
}
// TODO:
TsConstructSignatureDecl(_) => {}
TsIndexSignature(_) => {}
}
}
@ -439,6 +481,7 @@ impl Into<TsTypeDef> for &TsTypeLit {
methods,
properties,
call_signatures,
index_signatures,
};
TsTypeDef {
@ -475,7 +518,7 @@ impl Into<TsTypeDef> for &TsFnOrConstructorType {
let mut params = vec![];
for param in &ts_fn_type.params {
let param_def = ts_fn_param_to_param_def(param);
let param_def = ts_fn_param_to_param_def(param, None);
params.push(param_def);
}
@ -494,7 +537,7 @@ impl Into<TsTypeDef> for &TsFnOrConstructorType {
let mut params = vec![];
for param in &ctor_type.params {
let param_def = ts_fn_param_to_param_def(param);
let param_def = ts_fn_param_to_param_def(param, None);
params.push(param_def);
}
@ -619,6 +662,21 @@ pub struct LiteralMethodDef {
pub type_params: Vec<TsTypeParamDef>,
}
impl Display for LiteralMethodDef {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
write!(
f,
"{}({})",
self.name,
SliceDisplayer::new(&self.params, ", ", false)
)?;
if let Some(return_type) = &self.return_type {
write!(f, ": {}", return_type)?;
}
Ok(())
}
}
#[derive(Debug, Serialize, Clone)]
#[serde(rename_all = "camelCase")]
pub struct LiteralPropertyDef {
@ -630,6 +688,15 @@ pub struct LiteralPropertyDef {
pub type_params: Vec<TsTypeParamDef>,
}
impl Display for LiteralPropertyDef {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
write!(f, "{}", self.name)?;
if let Some(ts_type) = &self.ts_type {
write!(f, ": {}", ts_type)?;
}
Ok(())
}
}
#[derive(Debug, Serialize, Clone)]
#[serde(rename_all = "camelCase")]
pub struct LiteralCallSignatureDef {
@ -638,12 +705,46 @@ pub struct LiteralCallSignatureDef {
pub type_params: Vec<TsTypeParamDef>,
}
impl Display for LiteralCallSignatureDef {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
write!(f, "({})", SliceDisplayer::new(&self.params, ", ", false))?;
if let Some(ts_type) = &self.ts_type {
write!(f, ": {}", ts_type)?;
}
Ok(())
}
}
#[derive(Debug, Serialize, Clone)]
#[serde(rename_all = "camelCase")]
pub struct LiteralIndexSignatureDef {
pub readonly: bool,
pub params: Vec<ParamDef>,
pub ts_type: Option<TsTypeDef>,
}
impl Display for LiteralIndexSignatureDef {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
write!(
f,
"{}[{}]",
display_readonly(self.readonly),
SliceDisplayer::new(&self.params, ", ", false)
)?;
if let Some(ts_type) = &self.ts_type {
write!(f, ": {}", ts_type)?;
}
Ok(())
}
}
#[derive(Debug, Serialize, Clone)]
#[serde(rename_all = "camelCase")]
pub struct TsTypeLiteralDef {
pub methods: Vec<LiteralMethodDef>,
pub properties: Vec<LiteralPropertyDef>,
pub call_signatures: Vec<LiteralCallSignatureDef>,
pub index_signatures: Vec<LiteralIndexSignatureDef>,
}
#[derive(Debug, PartialEq, Serialize, Clone)]
@ -753,3 +854,133 @@ pub fn ts_type_ann_to_def(type_ann: &TsTypeAnn) -> TsTypeDef {
},
}
}
impl Display for TsTypeDef {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
if self.kind.is_none() {
return write!(f, "{}", colors::red("<UNIMPLEMENTED>"));
}
let kind = self.kind.as_ref().unwrap();
match kind {
TsTypeDefKind::Array => write!(f, "{}[]", &*self.array.as_ref().unwrap()),
TsTypeDefKind::Conditional => {
let conditional = self.conditional_type.as_ref().unwrap();
write!(
f,
"{} {} {} ? {} : {}",
&*conditional.check_type,
colors::magenta("extends"),
&*conditional.extends_type,
&*conditional.true_type,
&*conditional.false_type
)
}
TsTypeDefKind::FnOrConstructor => {
let fn_or_constructor = self.fn_or_constructor.as_ref().unwrap();
write!(
f,
"{}({}) => {}",
colors::magenta(if fn_or_constructor.constructor {
"new "
} else {
""
}),
SliceDisplayer::new(&fn_or_constructor.params, ", ", false),
&fn_or_constructor.ts_type,
)
}
TsTypeDefKind::IndexedAccess => {
let indexed_access = self.indexed_access.as_ref().unwrap();
write!(
f,
"{}[{}]",
&*indexed_access.obj_type, &*indexed_access.index_type
)
}
TsTypeDefKind::Intersection => {
let intersection = self.intersection.as_ref().unwrap();
write!(f, "{}", SliceDisplayer::new(&intersection, " & ", false))
}
TsTypeDefKind::Keyword => {
write!(f, "{}", colors::cyan(self.keyword.as_ref().unwrap()))
}
TsTypeDefKind::Literal => {
let literal = self.literal.as_ref().unwrap();
match literal.kind {
doc::ts_type::LiteralDefKind::Boolean => write!(
f,
"{}",
colors::yellow(&literal.boolean.unwrap().to_string())
),
doc::ts_type::LiteralDefKind::String => write!(
f,
"{}",
colors::green(&format!("\"{}\"", literal.string.as_ref().unwrap()))
),
doc::ts_type::LiteralDefKind::Number => write!(
f,
"{}",
colors::yellow(&literal.number.unwrap().to_string())
),
}
}
TsTypeDefKind::Optional => {
write!(f, "{}?", &*self.optional.as_ref().unwrap())
}
TsTypeDefKind::Parenthesized => {
write!(f, "({})", &*self.parenthesized.as_ref().unwrap())
}
TsTypeDefKind::Rest => write!(f, "...{}", &*self.rest.as_ref().unwrap()),
TsTypeDefKind::This => write!(f, "this"),
TsTypeDefKind::Tuple => {
let tuple = self.tuple.as_ref().unwrap();
write!(f, "[{}]", SliceDisplayer::new(&tuple, ", ", false))
}
TsTypeDefKind::TypeLiteral => {
let type_literal = self.type_literal.as_ref().unwrap();
write!(
f,
"{{ {}{}{}{}}}",
SliceDisplayer::new(&type_literal.call_signatures, "; ", true),
SliceDisplayer::new(&type_literal.methods, "; ", true),
SliceDisplayer::new(&type_literal.properties, "; ", true),
SliceDisplayer::new(&type_literal.index_signatures, "; ", true),
)
}
TsTypeDefKind::TypeOperator => {
let operator = self.type_operator.as_ref().unwrap();
write!(f, "{} {}", operator.operator, &operator.ts_type)
}
TsTypeDefKind::TypeQuery => {
write!(f, "typeof {}", self.type_query.as_ref().unwrap())
}
TsTypeDefKind::TypeRef => {
let type_ref = self.type_ref.as_ref().unwrap();
write!(f, "{}", colors::intense_blue(&type_ref.type_name))?;
if let Some(type_params) = &type_ref.type_params {
write!(f, "<{}>", SliceDisplayer::new(type_params, ", ", false))?;
}
Ok(())
}
TsTypeDefKind::Union => {
let union = self.union.as_ref().unwrap();
write!(f, "{}", SliceDisplayer::new(union, " | ", false))
}
}
}
}
pub fn maybe_type_param_instantiation_to_type_defs(
maybe_type_param_instantiation: Option<&TsTypeParamInstantiation>,
) -> Vec<TsTypeDef> {
if let Some(type_param_instantiation) = maybe_type_param_instantiation {
type_param_instantiation
.params
.iter()
.map(|type_param| type_param.as_ref().into())
.collect::<Vec<TsTypeDef>>()
} else {
vec![]
}
}

View file

@ -3,6 +3,7 @@ use super::ts_type::TsTypeDef;
use crate::swc_ecma_ast::TsTypeParam;
use crate::swc_ecma_ast::TsTypeParamDecl;
use serde::Serialize;
use std::fmt::{Display, Formatter, Result as FmtResult};
#[derive(Debug, Serialize, Clone)]
#[serde(rename_all = "camelCase")]
@ -16,6 +17,19 @@ pub struct TsTypeParamDef {
pub default: Option<TsTypeDef>,
}
impl Display for TsTypeParamDef {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
write!(f, "{}", self.name)?;
if let Some(constraint) = &self.constraint {
write!(f, " extends {}", constraint)?;
}
if let Some(default) = &self.default {
write!(f, " = {}", default)?;
}
Ok(())
}
}
impl Into<TsTypeParamDef> for &TsTypeParam {
fn into(self) -> TsTypeParamDef {
let name = self.name.sym.to_string();

View file

@ -23,6 +23,7 @@ pub enum DenoSubcommand {
buf: Box<[u8]>,
},
Doc {
private: bool,
json: bool,
source_file: Option<String>,
filter: Option<String>,
@ -598,12 +599,14 @@ fn doc_parse(flags: &mut Flags, matches: &clap::ArgMatches) {
unstable_arg_parse(flags, matches);
let source_file = matches.value_of("source_file").map(String::from);
let private = matches.is_present("private");
let json = matches.is_present("json");
let filter = matches.value_of("filter").map(String::from);
flags.subcommand = DenoSubcommand::Doc {
source_file,
json,
filter,
private,
};
}
@ -915,6 +918,9 @@ fn doc_subcommand<'a, 'b>() -> App<'a, 'b> {
Output documentation to standard output:
deno doc ./path/to/module.ts
Output private documentation to standard output:
deno doc --private ./path/to/module.ts
Output documentation in JSON format:
deno doc --json ./path/to/module.ts
@ -932,6 +938,12 @@ Show documentation for runtime built-ins:
.help("Output documentation in JSON format.")
.takes_value(false),
)
.arg(
Arg::with_name("private")
.long("private")
.help("Output private documentation")
.takes_value(false),
)
// TODO(nayeemrmn): Make `--builtin` a proper option. Blocked by
// https://github.com/clap-rs/clap/issues/1794. Currently `--builtin` is
// just a possible value of `source_file` so leading hyphens must be
@ -2910,6 +2922,7 @@ mod tests {
r.unwrap(),
Flags {
subcommand: DenoSubcommand::Doc {
private: false,
json: true,
source_file: Some("path/to/module.ts".to_string()),
filter: None,
@ -2928,6 +2941,7 @@ mod tests {
r.unwrap(),
Flags {
subcommand: DenoSubcommand::Doc {
private: false,
json: false,
source_file: Some("path/to/module.ts".to_string()),
filter: Some("SomeClass.someField".to_string()),
@ -2941,6 +2955,7 @@ mod tests {
r.unwrap(),
Flags {
subcommand: DenoSubcommand::Doc {
private: false,
json: false,
source_file: None,
filter: None,
@ -2955,6 +2970,7 @@ mod tests {
r.unwrap(),
Flags {
subcommand: DenoSubcommand::Doc {
private: false,
json: false,
source_file: Some("--builtin".to_string()),
filter: Some("Deno.Listener".to_string()),
@ -2962,6 +2978,25 @@ mod tests {
..Flags::default()
}
);
let r = flags_from_vec_safe(svec![
"deno",
"doc",
"--private",
"path/to/module.js"
]);
assert_eq!(
r.unwrap(),
Flags {
subcommand: DenoSubcommand::Doc {
private: true,
json: false,
source_file: Some("path/to/module.js".to_string()),
filter: None,
},
..Flags::default()
}
);
}
#[test]

View file

@ -521,6 +521,7 @@ async fn doc_command(
source_file: Option<String>,
json: bool,
maybe_filter: Option<String>,
private: bool,
) -> Result<(), ErrBox> {
let global_state = GlobalState::new(flags.clone())?;
let source_file = source_file.unwrap_or_else(|| "--builtin".to_string());
@ -546,7 +547,7 @@ async fn doc_command(
}
let loader = Box::new(global_state.file_fetcher.clone());
let doc_parser = doc::DocParser::new(loader);
let doc_parser = doc::DocParser::new(loader, private);
let parse_result = if source_file == "--builtin" {
doc_parser.parse_source("lib.deno.d.ts", get_types(flags.unstable).as_str())
@ -576,13 +577,9 @@ async fn doc_command(
eprintln!("Node {} was not found!", filter);
std::process::exit(1);
}
let mut details = String::new();
for node in nodes {
details.push_str(doc::printer::format_details(node).as_str());
}
details
format!("{}", doc::DocPrinter::new(&nodes, true, private))
} else {
doc::printer::format(doc_nodes)
format!("{}", doc::DocPrinter::new(&doc_nodes, false, private))
};
write_to_stdout_ignore_sigpipe(details.as_bytes()).map_err(ErrBox::from)
@ -720,7 +717,8 @@ pub fn main() {
source_file,
json,
filter,
} => doc_command(flags, source_file, json, filter).boxed_local(),
private,
} => doc_command(flags, source_file, json, filter, private).boxed_local(),
DenoSubcommand::Eval {
print,
code,