1#![warn(missing_docs)]
2use bitflags::bitflags;
3
4use oxc_allocator::{Allocator, CloneIn};
5use oxc_ast_macros::ast;
6use oxc_estree::ESTree;
7use oxc_span::{ContentEq, Span};
8
9#[ast]
11#[generate_derive(CloneIn, ContentEq, ESTree)]
12#[derive(Debug, Default, Clone, Copy, Eq, PartialEq)]
13#[estree(no_rename_variants, no_ts_def)]
14pub enum CommentKind {
15 #[default]
17 Line = 0,
18 Block = 1,
20}
21
22#[ast]
24#[generate_derive(CloneIn, ContentEq)]
25#[derive(Debug, Default, Clone, Copy, Eq, PartialEq)]
26pub enum CommentPosition {
27 #[default]
38 Leading = 0,
39
40 Trailing = 1,
43}
44
45#[ast]
47#[generate_derive(CloneIn, ContentEq)]
48#[derive(Debug, Default, Clone, Copy, Eq, PartialEq)]
49pub enum CommentContent {
50 #[default]
52 None = 0,
53
54 Legal = 1,
58
59 Jsdoc = 2,
62
63 JsdocLegal = 3,
66
67 Pure = 4,
70
71 NoSideEffects = 5,
73
74 Webpack = 6,
78
79 Vite = 7,
83
84 CoverageIgnore = 8,
88}
89
90bitflags! {
91 #[derive(Default, Debug, Clone, Copy, Eq, PartialEq)]
92 pub struct CommentNewlines: u8 {
94 const Leading = 1 << 0;
96 const Trailing = 1 << 1;
98 const None = 0;
100 }
101}
102
103#[ast(foreign = CommentNewlines)]
105#[expect(dead_code)]
106struct CommentNewlinesAlias(u8);
107
108impl ContentEq for CommentNewlines {
109 fn content_eq(&self, other: &Self) -> bool {
110 self == other
111 }
112}
113
114impl<'alloc> CloneIn<'alloc> for CommentNewlines {
115 type Cloned = Self;
116
117 fn clone_in(&self, _: &'alloc Allocator) -> Self::Cloned {
118 *self
119 }
120}
121
122#[ast]
124#[generate_derive(CloneIn, ContentEq, ESTree)]
125#[derive(Debug, Default, Clone, Copy, Eq, PartialEq)]
126#[estree(add_fields(value = CommentValue), no_ts_def, no_parent)]
127pub struct Comment {
128 pub span: Span,
130
131 #[estree(skip)]
136 pub attached_to: u32,
137
138 #[estree(rename = "type")]
140 pub kind: CommentKind,
141
142 #[estree(skip)]
144 pub position: CommentPosition,
145
146 #[estree(skip)]
149 pub newlines: CommentNewlines,
150
151 #[estree(skip)]
153 pub content: CommentContent,
154}
155
156impl Comment {
157 #[inline]
159 pub fn new(start: u32, end: u32, kind: CommentKind) -> Self {
160 let span = Span::new(start, end);
161 Self {
162 span,
163 attached_to: 0,
164 kind,
165 position: CommentPosition::Trailing,
166 newlines: CommentNewlines::None,
167 content: CommentContent::None,
168 }
169 }
170
171 pub fn content_span(&self) -> Span {
173 match self.kind {
174 CommentKind::Line => Span::new(self.span.start + 2, self.span.end),
175 CommentKind::Block => Span::new(self.span.start + 2, self.span.end - 2),
176 }
177 }
178
179 #[inline]
181 pub fn is_line(self) -> bool {
182 self.kind == CommentKind::Line
183 }
184
185 #[inline]
187 pub fn is_block(self) -> bool {
188 self.kind == CommentKind::Block
189 }
190
191 #[inline]
193 pub fn is_leading(self) -> bool {
194 self.position == CommentPosition::Leading
195 }
196
197 #[inline]
199 pub fn is_trailing(self) -> bool {
200 self.position == CommentPosition::Trailing
201 }
202
203 #[inline]
205 pub fn is_normal(self) -> bool {
206 self.content == CommentContent::None
207 }
208
209 #[inline]
211 pub fn is_annotation(self) -> bool {
212 self.content != CommentContent::None
213 && self.content != CommentContent::Legal
214 && self.content != CommentContent::Jsdoc
215 && self.content != CommentContent::JsdocLegal
216 }
217
218 #[inline]
220 pub fn is_jsdoc(self) -> bool {
221 matches!(self.content, CommentContent::Jsdoc | CommentContent::JsdocLegal)
222 && self.is_leading()
223 }
224
225 #[inline]
232 pub fn is_legal(self) -> bool {
233 matches!(self.content, CommentContent::Legal | CommentContent::JsdocLegal)
234 && self.is_leading()
235 }
236
237 #[inline]
239 pub fn is_pure(self) -> bool {
240 self.content == CommentContent::Pure
241 }
242
243 #[inline]
245 pub fn is_no_side_effects(self) -> bool {
246 self.content == CommentContent::NoSideEffects
247 }
248
249 #[inline]
251 pub fn is_webpack(self) -> bool {
252 self.content == CommentContent::Webpack
253 }
254
255 #[inline]
257 pub fn is_vite(self) -> bool {
258 self.content == CommentContent::Vite
259 }
260
261 #[inline]
263 pub fn is_coverage_ignore(self) -> bool {
264 self.content == CommentContent::CoverageIgnore && self.is_leading()
265 }
266
267 #[inline]
269 pub fn preceded_by_newline(self) -> bool {
270 self.newlines.contains(CommentNewlines::Leading)
271 }
272
273 #[inline]
275 pub fn followed_by_newline(self) -> bool {
276 self.newlines.contains(CommentNewlines::Trailing)
277 }
278
279 #[inline]
281 pub fn set_preceded_by_newline(&mut self, preceded_by_newline: bool) {
282 self.newlines.set(CommentNewlines::Leading, preceded_by_newline);
283 }
284
285 #[inline]
287 pub fn set_followed_by_newline(&mut self, followed_by_newline: bool) {
288 self.newlines.set(CommentNewlines::Trailing, followed_by_newline);
289 }
290}