1use super::{ASTNode, Span, TSXToken, TokenReader};
4use crate::{ParseOptions, ParseResult};
5
6use tokenizer_lib::Token;
7use visitable_derive::Visitable;
8
9#[cfg_attr(target_family = "wasm", derive(tsify::Tsify))]
10#[derive(Debug, Clone, Visitable)]
11pub enum WithComment<T> {
12 None(T),
13 PrefixComment(String, T, Span),
14 PostfixComment(T, String, Span),
15}
16
17#[cfg(feature = "self-rust-tokenize")]
19impl<T> self_rust_tokenize::SelfRustTokenize for WithComment<T>
20where
21 T: self_rust_tokenize::SelfRustTokenize,
22{
23 fn append_to_token_stream(
24 &self,
25 token_stream: &mut self_rust_tokenize::proc_macro2::TokenStream,
26 ) {
27 let inner = self_rust_tokenize::SelfRustTokenize::to_tokens(self.get_ast_ref());
28 token_stream.extend(self_rust_tokenize::quote!(WithComment::None(#inner)));
29 }
30}
31
32#[cfg(feature = "serde-serialize")]
34impl<T: serde::Serialize> serde::Serialize for WithComment<T> {
35 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
36 where
37 S: serde::Serializer,
38 {
39 self.get_ast_ref().serialize(serializer)
40 }
41}
42
43impl<T: PartialEq> PartialEq for WithComment<T> {
44 fn eq(&self, other: &Self) -> bool {
45 self.get_ast_ref() == other.get_ast_ref()
46 }
47}
48
49impl<T> From<T> for WithComment<T> {
50 fn from(item: T) -> Self {
51 Self::None(item)
52 }
53}
54
55impl<T> WithComment<T> {
56 pub fn get_ast(self) -> T {
57 match self {
58 Self::None(ast) | Self::PrefixComment(_, ast, _) | Self::PostfixComment(ast, _, _) => {
59 ast
60 }
61 }
62 }
63
64 pub fn get_ast_ref(&self) -> &T {
65 match self {
66 Self::None(ast) | Self::PrefixComment(_, ast, _) | Self::PostfixComment(ast, _, _) => {
67 ast
68 }
69 }
70 }
71
72 pub fn get_ast_mut(&mut self) -> &mut T {
73 match self {
74 Self::None(ast) | Self::PrefixComment(_, ast, _) | Self::PostfixComment(ast, _, _) => {
75 ast
76 }
77 }
78 }
79
80 pub fn map<U>(self, cb: impl FnOnce(T) -> U) -> WithComment<U> {
81 match self {
82 Self::None(item) => WithComment::None(cb(item)),
83 Self::PrefixComment(comment, item, position) => {
84 WithComment::PrefixComment(comment, cb(item), position)
85 }
86 Self::PostfixComment(item, comment, position) => {
87 WithComment::PostfixComment(cb(item), comment, position)
88 }
89 }
90 }
91}
92
93impl<T: ASTNode> ASTNode for WithComment<T> {
94 fn from_reader(
95 reader: &mut impl TokenReader<TSXToken, crate::TokenStart>,
96 state: &mut crate::ParsingState,
97 options: &ParseOptions,
98 ) -> ParseResult<Self> {
99 if let Some(token) =
100 reader.conditional_next(|t| matches!(t, TSXToken::MultiLineComment(..)))
101 {
102 let Token(TSXToken::MultiLineComment(comment), position) = token else {
103 unreachable!();
104 };
105 let item = T::from_reader(reader, state, options)?;
106 let position = position.union(item.get_position());
107 Ok(Self::PrefixComment(comment, item, position))
108 } else {
109 let item = T::from_reader(reader, state, options)?;
110 if let Some(token) =
111 reader.conditional_next(|t| matches!(t, TSXToken::MultiLineComment(..)))
112 {
113 let end = token.get_span();
114 let Token(TSXToken::MultiLineComment(comment), _) = token else {
115 unreachable!();
116 };
117 let position = item.get_position().union(end);
118 Ok(Self::PostfixComment(item, comment, position))
119 } else {
120 Ok(Self::None(item))
121 }
122 }
123 }
124
125 fn get_position(&self) -> Span {
126 match self {
127 Self::None(ast) => ast.get_position(),
128 Self::PostfixComment(_, _, position) | Self::PrefixComment(_, _, position) => *position,
129 }
130 }
131
132 fn to_string_from_buffer<U: source_map::ToString>(
133 &self,
134 buf: &mut U,
135 options: &crate::ToStringOptions,
136 local: crate::LocalToStringInformation,
137 ) {
138 match self {
139 Self::None(ast) => ast.to_string_from_buffer(buf, options, local),
140 Self::PrefixComment(content, ast, _) => {
141 if options.should_add_comment(content) {
142 buf.push_str("/*");
143 if options.pretty {
144 for (idx, line) in content.split('\n').enumerate() {
147 if idx > 0 {
148 buf.push_new_line();
149 }
150 options.add_indent(local.depth, buf);
151 buf.push_str(line.trim());
152 }
153 } else {
155 buf.push_str_contains_new_line(content.as_str());
156 }
157 buf.push_str("*/");
158 }
159 ast.to_string_from_buffer(buf, options, local);
160 }
161 Self::PostfixComment(ast, comment, _) => {
162 ast.to_string_from_buffer(buf, options, local);
163 if options.should_add_comment(comment) {
164 buf.push_str("/*");
165 if options.pretty {
166 for (idx, line) in comment.split('\n').enumerate() {
168 if idx > 0 {
169 buf.push_new_line();
170 }
171 options.add_indent(local.depth, buf);
172 buf.push_str(line.trim());
173 }
174 } else {
175 buf.push_str_contains_new_line(comment.as_str());
176 }
177 buf.push_str("*/");
178 }
179 }
180 }
181 }
182}