use super::*;
use crate::ast::{ArgumentName, AstString};
#[derive(Default)]
pub(super) struct NamedTypeListCst<'a> {
pub(super) open_parenthesis: Option<Position>,
pub(super) close_parenthesis: Option<Position>,
pub(super) comma_positions: Option<&'a [Position]>,
pub(super) arg_name_colons: Option<&'a [Option<Position>]>,
}
impl<'cst, 'ast> Printer<'cst, 'ast> {
pub(super) fn write_type_list(
&mut self,
type_list: TypeList,
unconditionally_parenthesize: bool,
open_parenthesis: Option<Position>,
close_parenthesis: Option<Position>,
comma_positions: Option<&[Position]>,
) {
self.maybe_advance_and_write(open_parenthesis, "(", unconditionally_parenthesize);
for (index, ty) in type_list.types.iter().enumerate() {
if index > 0 {
if let Some(position) =
comma_positions.and_then(|positions| positions.get(index - 1))
{
self.advance(*position);
}
self.symbol(",");
}
self.write_type(*ty);
}
if let Some(tail) = type_list.tail_type {
if !type_list.types.is_empty() {
if let Some(position) =
comma_positions.and_then(|positions| positions.get(type_list.types.len() - 1))
{
self.advance(*position);
}
self.symbol(",");
}
self.write_type_pack(tail, false);
}
self.maybe_advance_and_write(close_parenthesis, ")", unconditionally_parenthesize);
}
pub(super) fn write_type_pack(&mut self, pack: TypePack, for_vararg: bool) {
self.advance(pack.location.begin);
let cst_nodes = self.cst_nodes;
match pack.kind() {
TypePackKind::Explicit { type_list }
if type_list.types.len() == 1 && type_list.tail_type.is_none() =>
{
if let Some(CstNode::TypePackExplicit(CstTypePackExplicit {
parentheses: Some(parentheses),
comma_positions,
})) = type_pack_cst(cst_nodes, pack)
{
self.write_type_list(
type_list,
false,
Some(parentheses.open),
Some(parentheses.close),
Some(comma_positions.as_slice()),
);
} else {
self.write_type(type_list.types[0]);
}
}
TypePackKind::Explicit { type_list } => {
if let Some(CstNode::TypePackExplicit(CstTypePackExplicit {
parentheses,
comma_positions,
})) = type_pack_cst(cst_nodes, pack)
{
self.write_type_list(
type_list,
false,
parentheses.map(|parentheses| parentheses.open),
parentheses.map(|parentheses| parentheses.close),
Some(comma_positions.as_slice()),
);
} else {
self.write_type_list(type_list, true, None, None, None);
}
}
TypePackKind::Variadic { variadic_type } => {
if !for_vararg {
self.symbol("...");
}
self.write_type(variadic_type);
}
TypePackKind::Generic { generic_name } => {
self.identifier(generic_name.bytes());
if let Some(CstNode::TypePackGeneric(CstTypePackGeneric { ellipsis })) =
type_pack_cst(cst_nodes, pack)
{
self.advance(*ellipsis);
}
self.symbol("...");
}
}
}
pub(super) fn write_type_or_pack(&mut self, value: TypeOrPack) {
match value {
TypeOrPack::Type(ty) => self.write_type(ty),
TypeOrPack::Pack(pack) => self.write_type_pack(pack, false),
}
}
pub(super) fn write_type(&mut self, annotation: Type) {
self.advance(annotation.location.begin);
let cst_nodes = self.cst_nodes;
match annotation.kind() {
TypeKind::Reference {
prefix,
name,
name_location,
parameters,
has_parameter_list,
..
} => {
let reference_cst = match type_cst(cst_nodes, annotation) {
Some(CstNode::TypeReference(cst)) => Some(cst),
_ => None,
};
if let Some(prefix) = prefix {
self.identifier(prefix.bytes());
if let Some(prefix_dot) = reference_cst.as_ref().and_then(|cst| cst.prefix_dot)
{
self.advance(prefix_dot);
}
self.symbol(".");
}
self.advance(name_location.begin);
self.identifier(name.bytes());
if has_parameter_list {
if let Some(cst) = reference_cst.as_ref() {
self.advance(cst.open_parameters);
}
self.symbol("<");
for (index, parameter) in parameters.iter().enumerate() {
if index > 0 {
if let Some(cst) = reference_cst.as_ref()
&& let Some(position) = cst.parameter_commas.get(index - 1)
{
self.advance(*position);
}
self.symbol(",");
}
self.write_type_or_pack(*parameter);
}
if let Some(cst) = reference_cst.as_ref() {
self.maybe_advance_and_write(Some(cst.close_parameters), ">", false);
} else {
self.symbol(">");
}
}
}
TypeKind::SingletonBool { value } => self.keyword(if value { "true" } else { "false" }),
TypeKind::SingletonString { value } => {
let string_cst: Option<(AstString<'ast>, CstStringQuoteStyle, u32)> =
match type_cst(cst_nodes, annotation) {
Some(CstNode::TypeSingletonString(CstTypeSingletonString {
source_string,
quote_style,
block_depth,
..
})) => Some((*source_string, *quote_style, *block_depth)),
_ => None,
};
if let Some((source_string, quote_style, block_depth)) = string_cst {
self.write_source_string(source_string.as_bytes(), quote_style, block_depth);
} else {
self.write_string(value.as_bytes())
}
}
TypeKind::Group { ty } => {
self.symbol("(");
self.write_type(ty);
if let Some(CstNode::TypeGroup(CstTypeGroup { close_position })) =
type_cst(cst_nodes, annotation)
{
self.maybe_advance_and_write(Some(*close_position), ")", false);
} else {
self.advance_before(annotation.location.end, 1);
self.symbol(")");
}
}
TypeKind::Union { types } => {
let union_cst = match type_cst(cst_nodes, annotation) {
Some(CstNode::TypeUnion(cst)) => Some(cst),
_ => None,
};
if let Some(leading) = union_cst.as_ref().and_then(|cst| cst.leading) {
self.advance(leading);
self.symbol("|");
}
for (index, ty) in types.iter().enumerate() {
if matches!(ty.kind(), TypeKind::Optional) {
self.write_type(*ty);
continue;
}
if index > 0 {
if let Some(cst) = union_cst.as_ref()
&& let Some(position) = cst.separators.get(index - 1)
{
self.advance(*position);
} else {
self.maybe_space(ty.location.begin, 2);
}
self.symbol("|");
}
self.write_type(*ty);
}
}
TypeKind::Intersection { types } => {
let intersection_cst = match type_cst(cst_nodes, annotation) {
Some(CstNode::TypeIntersection(cst)) => Some(cst),
_ => None,
};
if let Some(leading) = intersection_cst.as_ref().and_then(|cst| cst.leading) {
self.advance(leading);
self.symbol("&");
}
for (index, ty) in types.iter().enumerate() {
if index > 0 {
if let Some(cst) = intersection_cst.as_ref()
&& let Some(position) = cst.separators.get(index - 1)
{
self.advance(*position);
} else {
self.maybe_space(ty.location.begin, 2);
}
self.symbol("&");
}
self.write_type(*ty);
}
}
TypeKind::Optional => self.symbol("?"),
TypeKind::Error { .. } => self.symbol("%error-type%"),
TypeKind::Table { props, indexer } => {
self.symbol("{");
let table_cst = match type_cst(cst_nodes, annotation) {
Some(CstNode::TypeTable(cst)) => Some(cst),
_ => None,
};
if let Some(cst) = table_cst {
if cst.is_array {
if let Some(indexer) = indexer {
if let Some(access) = indexer.access_location {
self.advance(access.begin);
self.write_table_access(indexer.access);
}
self.write_type(indexer.result_type);
}
} else {
let mut props = props.iter();
for item in &cst.items {
match item.kind {
CstTypeTableItemKind::Indexer => {
if let Some(indexer) = indexer {
if let Some(access) = indexer.access_location {
self.advance(access.begin);
self.write_table_access(indexer.access);
}
self.advance(item.indexer_open);
self.symbol("[");
self.write_type(indexer.index_type);
self.maybe_advance_and_write(
Some(item.indexer_close),
"]",
false,
);
self.maybe_advance_and_write(Some(item.colon), ":", false);
self.write_type(indexer.result_type);
}
}
CstTypeTableItemKind::Property
| CstTypeTableItemKind::StringProperty => {
if let Some(prop) = props.next() {
if let Some(access) = prop.access_location {
self.advance(access.begin);
self.write_table_access(prop.access);
}
if item.kind == CstTypeTableItemKind::StringProperty {
self.advance(item.indexer_open);
self.symbol("[");
if let Some(string_info) = &item.string_info {
self.advance(item.string_position);
self.write_source_string(
string_info.source_string.as_bytes(),
string_info.quote_style,
string_info.block_depth,
);
}
self.maybe_advance_and_write(
Some(item.indexer_close),
"]",
false,
);
} else {
self.advance(prop.location.begin);
self.identifier(prop.name.bytes());
}
self.maybe_advance_and_write(Some(item.colon), ":", false);
self.write_type(prop.ty);
}
}
}
if let Some(separator) = item.separator {
if let Some(position) = item.separator_position {
self.advance(position);
}
self.symbol(match separator {
TableSeparator::Comma => ",",
TableSeparator::Semicolon => ";",
});
}
}
}
} else {
for (index, prop) in props.iter().enumerate() {
if index > 0 {
self.symbol(",");
}
if let Some(access) = prop.access_location {
self.advance(access.begin);
self.write_table_access(prop.access);
}
self.advance(prop.location.begin);
self.identifier(prop.name.bytes());
self.symbol(":");
self.write_type(prop.ty);
}
if let Some(indexer) = indexer {
if !props.is_empty() {
self.symbol(",");
}
if let Some(access) = indexer.access_location {
self.advance(access.begin);
self.write_table_access(indexer.access);
}
self.symbol("[");
self.write_type(indexer.index_type);
self.symbol("]:");
self.write_type(indexer.result_type);
}
}
self.advance_before(annotation.location.end, 1);
self.symbol("}");
}
TypeKind::Function {
generics,
generic_packs,
arg_types,
arg_names,
return_types,
..
} => {
let function_cst = match type_cst(cst_nodes, annotation) {
Some(CstNode::TypeFunction(cst)) => Some(cst),
_ => None,
};
self.write_generic_parameters(
generics,
generic_packs,
function_cst.as_ref().map(|cst| {
(
cst.open_generics,
cst.generics_commas.as_slice(),
cst.close_generics,
)
}),
GenericPackEllipsisMode::AlwaysWrite,
);
self.write_named_type_list(
arg_types,
arg_names,
function_cst.is_none(),
NamedTypeListCst {
open_parenthesis: function_cst.as_ref().map(|cst| cst.open_arguments),
close_parenthesis: function_cst.as_ref().map(|cst| cst.close_arguments),
comma_positions: function_cst
.as_ref()
.map(|cst| cst.argument_commas.as_slice()),
arg_name_colons: function_cst
.as_ref()
.map(|cst| cst.argument_name_colons.as_slice()),
},
);
if let Some(cst) = function_cst {
self.advance(cst.return_arrow);
}
self.symbol("->");
self.write_type_pack(return_types, false);
}
TypeKind::Typeof { expr } => {
let typeof_cst = match type_cst(cst_nodes, annotation) {
Some(CstNode::TypeTypeof(cst)) => Some(cst),
_ => None,
};
self.keyword("typeof");
if let Some(cst) = typeof_cst.as_ref() {
self.maybe_advance_and_write(Some(cst.open), "(", false);
} else {
self.symbol("(");
}
self.write_expression(expr);
if let Some(cst) = typeof_cst.as_ref() {
self.maybe_advance_and_write(Some(cst.close), ")", false);
} else {
self.symbol(")");
}
}
}
}
pub(super) fn write_table_access(&mut self, access: TableAccess) {
match access {
TableAccess::Read => self.keyword("read"),
TableAccess::Write => self.keyword("write"),
TableAccess::ReadWrite => {}
}
}
pub(super) fn write_named_type_list(
&mut self,
type_list: TypeList,
arg_names: &[Option<ArgumentName>],
unconditionally_parenthesize: bool,
cst: NamedTypeListCst<'_>,
) {
self.maybe_advance_and_write(cst.open_parenthesis, "(", unconditionally_parenthesize);
for (index, ty) in type_list.types.iter().enumerate() {
if index > 0 {
if let Some(position) = cst
.comma_positions
.and_then(|positions| positions.get(index - 1))
{
self.advance(*position);
}
self.symbol(",");
}
if let Some(Some(arg_name)) = arg_names.get(index) {
self.advance(arg_name.location.begin);
self.identifier(arg_name.name.bytes());
if let Some(Some(position)) = cst
.arg_name_colons
.and_then(|positions| positions.get(index))
{
self.advance(*position);
}
self.symbol(":");
}
self.write_type(*ty);
}
if let Some(tail) = type_list.tail_type {
if !type_list.types.is_empty() {
if let Some(position) = cst
.comma_positions
.and_then(|positions| positions.get(type_list.types.len() - 1))
{
self.advance(*position);
}
self.symbol(",");
}
self.write_type_pack(tail, false);
}
self.maybe_advance_and_write(cst.close_parenthesis, ")", unconditionally_parenthesize);
}
}