Skip to main content

swc_ecma_codegen/text_writer/
semicolon.rs

1use swc_common::{BytePos, Span, DUMMY_SP};
2
3use super::{BindingStorage, Result, ScopeKind, WriteJs};
4
5pub fn omit_trailing_semi<W: WriteJs>(w: W) -> impl WriteJs {
6    OmitTrailingSemi {
7        inner: w,
8        pending_semi: None,
9    }
10}
11
12#[derive(Debug, Clone)]
13struct OmitTrailingSemi<W: WriteJs> {
14    inner: W,
15    pending_semi: Option<Span>,
16}
17
18macro_rules! with_semi {
19    (
20        $fn_name:ident
21        (
22            $(
23                $arg_name:ident
24                :
25                $arg_ty:ty
26            ),*
27        )
28    ) => {
29        fn $fn_name(&mut self, $($arg_name: $arg_ty),* ) -> Result {
30            self.commit_pending_semi()?;
31
32            self.inner.$fn_name( $($arg_name),* )
33        }
34    };
35}
36
37impl<W: WriteJs> WriteJs for OmitTrailingSemi<W> {
38    with_semi!(increase_indent());
39
40    with_semi!(decrease_indent());
41
42    with_semi!(write_space());
43
44    with_semi!(write_comment(s: &str));
45
46    with_semi!(write_keyword(span: Option<Span>, s: &'static str));
47
48    with_semi!(write_operator(span: Option<Span>, s: &str));
49
50    with_semi!(write_param(s: &str));
51
52    with_semi!(write_property(s: &str));
53
54    with_semi!(write_line());
55
56    with_semi!(write_lit(span: Span, s: &str));
57
58    with_semi!(write_str_lit(span: Span, s: &str));
59
60    with_semi!(write_str(s: &str));
61
62    with_semi!(write_symbol(span: Span, s: &str));
63
64    fn write_semi(&mut self, span: Option<Span>) -> Result {
65        self.pending_semi = Some(span.unwrap_or(DUMMY_SP));
66        Ok(())
67    }
68
69    fn write_punct(
70        &mut self,
71        span: Option<Span>,
72        s: &'static str,
73        commit_pending_semi: bool,
74    ) -> Result {
75        if commit_pending_semi {
76            self.commit_pending_semi()?;
77        } else {
78            self.pending_semi = None;
79        }
80        self.inner.write_punct(span, s, commit_pending_semi)
81    }
82
83    #[inline]
84    fn care_about_srcmap(&self) -> bool {
85        self.inner.care_about_srcmap()
86    }
87
88    #[inline]
89    fn add_srcmap(&mut self, pos: BytePos) -> Result {
90        self.inner.add_srcmap(pos)
91    }
92
93    fn commit_pending_semi(&mut self) -> Result {
94        if let Some(span) = self.pending_semi {
95            self.inner.write_semi(Some(span))?;
96            self.pending_semi = None;
97        }
98        Ok(())
99    }
100
101    #[inline(always)]
102    fn can_ignore_invalid_unicodes(&mut self) -> bool {
103        self.inner.can_ignore_invalid_unicodes()
104    }
105
106    #[inline(always)]
107    fn has_scope_tracking(&self) -> bool {
108        self.inner.has_scope_tracking()
109    }
110
111    #[inline]
112    fn start_scope(
113        &mut self,
114        name: Option<&str>,
115        kind: ScopeKind,
116        is_stack_frame: bool,
117        is_hidden: bool,
118        original_span: Option<Span>,
119    ) -> Result {
120        self.inner
121            .start_scope(name, kind, is_stack_frame, is_hidden, original_span)
122    }
123
124    #[inline]
125    fn end_scope(&mut self) -> Result {
126        self.inner.end_scope()
127    }
128
129    #[inline]
130    fn add_scope_variable(
131        &mut self,
132        name: &str,
133        expression: Option<&str>,
134        storage: BindingStorage,
135    ) -> Result {
136        self.inner.add_scope_variable(name, expression, storage)
137    }
138}
139
140#[cfg(test)]
141mod tests {
142    use std::sync::Arc;
143
144    use swc_common::SourceMap;
145
146    use crate::text_writer::{basic_impl::JsWriter, BindingStorage, ScopeKind, WriteJs};
147
148    #[test]
149    fn forwards_scope_operations() {
150        let source_map = Arc::new(SourceMap::default());
151        let mut out = vec![];
152        let mut scopes = vec![];
153        {
154            let writer =
155                JsWriter::new_with_scopes(source_map, "\n", &mut out, None, Some(&mut scopes));
156            let mut writer = super::omit_trailing_semi(writer);
157
158            writer
159                .start_scope(None, ScopeKind::Function, true, false, None)
160                .unwrap();
161            writer
162                .add_scope_variable("x", Some("x"), BindingStorage::Lexical)
163                .unwrap();
164            writer.end_scope().unwrap();
165        }
166
167        assert_eq!(scopes.len(), 1);
168        assert_eq!(scopes[0].bindings.len(), 1);
169        assert_eq!(scopes[0].bindings[0].name, "x");
170    }
171}