use super::Printer;
use crate::ast::{self, Expression, ExpressionKind};
use crate::printer::is_string_literal;
use crate::printer::needs_parens::{
LeftmostText, leftmost_no_lookahead, leftmost_no_lookahead_reached,
};
use tsv_lang::Span;
fn strip_statement_casts<'a>(expr: &'a Expression<'a>) -> Option<&'a Expression<'a>> {
let mut cur = expr;
let mut stripped = false;
loop {
cur = match &cur.kind {
ExpressionKind::TSAsExpression(e) => e.expression,
ExpressionKind::TSSatisfiesExpression(e) => e.expression,
_ => break,
};
stripped = true;
}
stripped.then_some(cur)
}
fn is_statement_ambiguous_keyword(name: &str) -> bool {
matches!(
name,
"type"
| "module"
| "namespace"
| "interface"
| "let"
| "using"
| "accessor"
| "readonly"
| "yield"
| "await"
| "component"
| "hook"
)
}
fn is_for_init_ambiguous_keyword(name: &str) -> bool {
matches!(name, "using" | "let")
}
impl<'a> Printer<'a> {
pub(in crate::printer::statements) fn statement_opens_with_paren(
&self,
span: Span,
expr_start: u32,
) -> bool {
span.start < expr_start && self.source.as_bytes().get(span.start as usize) == Some(&b'(')
}
pub(in crate::printer::statements) fn expr_stmt_nested_paren_target(
&self,
expression: &Expression<'_>,
text: LeftmostText,
) -> Option<Span> {
let reached = leftmost_no_lookahead_reached(expression, text);
let leftmost = reached.0;
if matches!(
leftmost.kind,
ExpressionKind::ObjectExpression(_)
| ExpressionKind::FunctionExpression(_)
| ExpressionKind::ClassExpression(_)
) {
return Some(leftmost.span());
}
let let_target = match text {
LeftmostText::Printed => self.let_bracket_target_of(reached),
LeftmostText::Frozen => self.let_bracket_head_target(expression),
};
let_target
.or_else(|| self.cast_head_keyword_target(expression, is_statement_ambiguous_keyword))
}
fn cast_head_keyword_target(
&self,
expression: &Expression<'_>,
is_ambiguous: fn(&str) -> bool,
) -> Option<Span> {
match strip_statement_casts(expression) {
Some(Expression {
kind: ExpressionKind::Identifier(id),
..
}) if self.with_ident_name(id, is_ambiguous) => Some(id.span),
_ => None,
}
}
pub(in crate::printer) fn let_bracket_head_target(
&self,
expression: &Expression<'_>,
) -> Option<Span> {
self.let_bracket_target_of(leftmost_no_lookahead_reached(
expression,
LeftmostText::Printed,
))
}
fn let_bracket_target_of(
&self,
(leftmost, is_computed_member_object): (&Expression<'_>, bool),
) -> Option<Span> {
let ExpressionKind::Identifier(id) = &leftmost.kind else {
return None;
};
(is_computed_member_object && self.with_ident_name(id, |name| name == "let"))
.then_some(id.span)
}
pub(in crate::printer) fn for_init_head_target(
&self,
expression: &Expression<'_>,
) -> Option<Span> {
self.let_bracket_head_target(expression)
.or_else(|| self.cast_head_keyword_target(expression, is_for_init_ambiguous_keyword))
}
pub(in crate::printer) fn for_in_of_let_head_target(
&self,
expression: &Expression<'_>,
) -> Option<Span> {
match &leftmost_no_lookahead(expression).kind {
ExpressionKind::Identifier(id) if self.with_ident_name(id, |name| name == "let") => {
Some(id.span)
}
_ => None,
}
}
pub(in crate::printer::statements) fn needs_avoid_directive_parens(
&self,
stmt: &ast::ExpressionStatement<'_>,
in_program_or_block: bool,
) -> bool {
in_program_or_block && is_string_literal(stmt.expression)
}
}