1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
use crate::prelude::*;

use crate::utils::{FormatLiteralStringToken, StringLiteralParentKind};

use crate::parentheses::NeedsParentheses;
use biome_js_syntax::JsStringLiteralExpressionFields;
use biome_js_syntax::{JsExpressionStatement, JsSyntaxKind};
use biome_js_syntax::{JsStringLiteralExpression, JsSyntaxNode};
use biome_rowan::AstNode;

#[derive(Debug, Clone, Default)]
pub(crate) struct FormatJsStringLiteralExpression;

impl FormatNodeRule<JsStringLiteralExpression> for FormatJsStringLiteralExpression {
    fn fmt_fields(
        &self,
        node: &JsStringLiteralExpression,
        f: &mut JsFormatter,
    ) -> FormatResult<()> {
        let JsStringLiteralExpressionFields { value_token } = node.as_fields();

        let value_token = value_token?;
        let formatted =
            FormatLiteralStringToken::new(&value_token, StringLiteralParentKind::Expression);

        formatted.fmt(f)
    }

    fn needs_parentheses(&self, item: &JsStringLiteralExpression) -> bool {
        item.needs_parentheses()
    }
}

impl NeedsParentheses for JsStringLiteralExpression {
    fn needs_parentheses_with_parent(&self, parent: &JsSyntaxNode) -> bool {
        if let Some(expression_statement) = JsExpressionStatement::cast_ref(parent) {
            return expression_statement
                .syntax()
                .parent()
                .map_or(false, |grand_parent| {
                    matches!(
                        grand_parent.kind(),
                        JsSyntaxKind::JS_STATEMENT_LIST | JsSyntaxKind::JS_MODULE_ITEM_LIST
                    )
                });
        }
        false
    }
}

#[cfg(test)]
mod tests {

    use crate::{assert_needs_parentheses, assert_not_needs_parentheses};
    use biome_js_syntax::{JsFileSource, JsStringLiteralExpression, ModuleKind};

    #[test]
    fn needs_parentheses() {
        assert_needs_parentheses!("{ 'test'; }", JsStringLiteralExpression);
        assert_needs_parentheses!(
            r#"
            {
                console.log(5);
                'test';
            }
            "#,
            JsStringLiteralExpression
        );
        assert_needs_parentheses!(
            r#"
            function Test () {
                ('test');
            }
            "#,
            JsStringLiteralExpression
        );
        assert_needs_parentheses!(
            r#"
            class A {
                static {
                    ('test');
                }
            }
            "#,
            JsStringLiteralExpression
        );
        assert_needs_parentheses!(
            "('test');",
            JsStringLiteralExpression,
            JsFileSource::ts().with_module_kind(ModuleKind::Module)
        );

        assert_not_needs_parentheses!("console.log('a')", JsStringLiteralExpression);
    }
}