diff options
author | Valentin Anger <syrupthinker@gryphno.de> | 2020-07-12 14:16:33 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-07-12 14:16:33 +0200 |
commit | 3374c73fba3a96df22d0c04e6c17078ca8cce45b (patch) | |
tree | 91d996554a2e276724a86ecb3bff19ea5411238b /cli/doc/printer.rs | |
parent | 871f9255e37b4d2e63439c84da8e9bed6b388034 (diff) |
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
Diffstat (limited to 'cli/doc/printer.rs')
-rw-r--r-- | cli/doc/printer.rs | 861 |
1 files changed, 392 insertions, 469 deletions
diff --git a/cli/doc/printer.rs b/cli/doc/printer.rs index b563a6adc..2e55c752a 100644 --- a/cli/doc/printer.rs +++ b/cli/doc/printer.rs @@ -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(); +impl<'a> DocPrinter<'a> { + pub fn new( + doc_nodes: &[doc::DocNode], + details: bool, + private: bool, + ) -> DocPrinter { + DocPrinter { + doc_nodes, + details, + private, + } + } - details.push_str(&format!( - "{}", - colors::gray(&format!( - "Defined in {}:{}:{} \n\n", - node.location.filename, node.location.line, node.location.col - )) - )); + 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) + } + } - details.push_str(&format_signature(&node, 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 + } + }); - 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"); + for node in sorted { + self.format_signature(w, &node, indent)?; - 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(js_doc) = &node.js_doc { + self.format_jsdoc(w, js_doc, indent + 1, self.details)?; + } - if let Some(extra) = maybe_extra { - details.push_str(&extra); - } + writeln!(w)?; - details -} + if DocNodeKind::Namespace == node.kind { + self.format_summary( + w, + &node.namespace_def.as_ref().unwrap().elements, + indent + 1, + )?; -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, - } -} + writeln!(w)?; + }; + } -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), + Ok(()) } -} -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) - } else { - kind_cmp + 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)?, + _ => {} + } } - }); - let mut output = String::new(); + Ok(()) + } - 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)); + 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, } - 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 += "?"; + 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) } - if let Some(ts_type) = param.ts_type { - rendered += ": "; - rendered += render_ts_type(ts_type).as_str(); + DocNodeKind::Namespace => { + self.format_namespace_signature(w, node, indent) } - 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) - ) + // 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))?; } - 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); + + 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)?; } - 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()) - } + 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)?; } } - TsTypeDefKind::Optional => { - format!("{}?", render_ts_type(*ts_type.optional.unwrap())) + for index_sign_def in &class_def.index_signatures { + writeln!(w, "{}{}", Indent(1), index_sign_def)?; } - TsTypeDefKind::Parenthesized => { - format!("({})", render_ts_type(*ts_type.parenthesized.unwrap())) + 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)?; + } } - TsTypeDefKind::Rest => { - format!("...{}", render_ts_type(*ts_type.rest.unwrap())) + 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))?; } - 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); + 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)?; } - 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() + 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)?; } - 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 + for index_sign_def in &interface_def.index_signatures { + writeln!(w, "{}{}", Indent(1), index_sign_def)?; } - 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); + 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)?; } - output } + writeln!(w) } -} -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 -} + 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) + )?; + } -// 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", " ")); + 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) + )?; + } - let mut js_doc = String::new(); + if !class_def.implements.is_empty() { + write!( + w, + " {} {}", + colors::magenta("implements"), + SliceDisplayer::new(&class_def.implements, ", ", false) + )?; + } - for line in lines { - js_doc.push_str(&add_indent(format!("{}\n", line), indent + 1)); + writeln!(w) } - 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, - )); + 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) + ) } - 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)); + 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) } - details.push_str("\n"); - details -} -fn format_namespace_details(node: doc::DocNode) -> String { - let mut ns = String::new(); + 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) + )?; + } - let elements = node.namespace_def.unwrap().elements; - for node in elements { - ns.push_str(&format_signature(&node, 1)); + writeln!(w) } - 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"), + 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), - render_params(function_def.params), - if let Some(return_type) = function_def.return_type { - format!(": {}", render_ts_type(return_type).as_str()) - } else { - "".to_string() - } - ), - indent, - ) -} + )?; + + if !type_alias_def.type_params.is_empty() { + write!( + w, + "<{}>", + SliceDisplayer::new(&type_alias_def.type_params, ", ", false) + )?; + } -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("") - }; - - let implements = &class_def.implements; - let implements_suffix = if !implements.is_empty() { - format!( - " {} {}", - colors::magenta("implements"), - colors::bold(&implements.join(", ")) - ) - } else { - String::from("") - }; + writeln!(w, " = {}", type_alias_def.ts_type) + } - add_indent( - format!( - "{} {}{}{}\n", - colors::magenta("class"), - colors::bold(&node.name), - extends_suffix, - implements_suffix, - ), - indent, - ) -} + 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(node: &doc::DocNode, indent: i64) -> String { - let variable_def = node.variable_def.clone().unwrap(); - add_indent( - format!( - "{} {}{}\n", + 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, - ) -} - -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, - ) + )?; + if let Some(ts_type) = &variable_def.ts_type { + write!(w, ": {}", ts_type)?; + } + writeln!(w) + } } -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) + } } |