use bitflags::bitflags;
use oxc_allocator::{Allocator, CloneIn};
use oxc_ast_macros::ast;
use oxc_estree::ESTree;
use oxc_span::{ContentEq, Span};
#[ast]
#[generate_derive(CloneIn, ContentEq, ESTree)]
#[derive(Debug, Default, Clone, Copy, Eq, PartialEq)]
#[estree(no_rename_variants, no_ts_def)]
pub enum CommentKind {
#[default]
Line = 0,
#[estree(rename = "Block")]
SingleLineBlock = 1,
#[estree(rename = "Block")]
MultiLineBlock = 2,
}
#[ast]
#[generate_derive(CloneIn, ContentEq)]
#[derive(Debug, Default, Clone, Copy, Eq, PartialEq)]
pub enum CommentPosition {
#[default]
Leading = 0,
Trailing = 1,
}
#[ast]
#[generate_derive(CloneIn, ContentEq)]
#[derive(Debug, Default, Clone, Copy, Eq, PartialEq)]
pub enum CommentContent {
#[default]
None = 0,
Legal = 1,
Jsdoc = 2,
JsdocLegal = 3,
Pure = 4,
PureNotApplied = 5,
NoSideEffects = 6,
Webpack = 7,
Vite = 8,
CoverageIgnore = 9,
Turbopack = 10,
}
bitflags! {
#[derive(Default, Debug, Clone, Copy, Eq, PartialEq)]
pub struct CommentNewlines: u8 {
const Leading = 1 << 0;
const Trailing = 1 << 1;
const None = 0;
}
}
#[ast(foreign = CommentNewlines)]
#[expect(dead_code)]
struct CommentNewlinesAlias(u8);
impl ContentEq for CommentNewlines {
fn content_eq(&self, other: &Self) -> bool {
self == other
}
}
impl<'alloc> CloneIn<'alloc> for CommentNewlines {
type Cloned = Self;
fn clone_in(&self, _: &'alloc Allocator) -> Self::Cloned {
*self
}
}
#[ast]
#[generate_derive(CloneIn, ContentEq, ESTree)]
#[derive(Debug, Default, Clone, Copy, Eq, PartialEq)]
#[estree(add_fields(value = CommentValue), no_ts_def, no_parent)]
pub struct Comment {
pub span: Span,
#[estree(skip)]
pub attached_to: u32,
#[estree(rename = "type")]
pub kind: CommentKind,
#[estree(skip)]
pub position: CommentPosition,
#[estree(skip)]
pub newlines: CommentNewlines,
#[estree(skip)]
pub content: CommentContent,
}
impl Comment {
#[inline]
pub fn new(start: u32, end: u32, kind: CommentKind) -> Self {
let span = Span::new(start, end);
Self {
span,
attached_to: 0,
kind,
position: CommentPosition::Trailing,
newlines: CommentNewlines::None,
content: CommentContent::None,
}
}
pub fn content_span(&self) -> Span {
match self.kind {
CommentKind::Line => Span::new(self.span.start + 2, self.span.end),
CommentKind::SingleLineBlock | CommentKind::MultiLineBlock => {
Span::new(self.span.start + 2, self.span.end - 2)
}
}
}
#[inline]
pub fn is_line(self) -> bool {
self.kind == CommentKind::Line
}
#[inline]
pub fn is_block(self) -> bool {
matches!(self.kind, CommentKind::SingleLineBlock | CommentKind::MultiLineBlock)
}
#[inline]
pub fn is_multiline_block(self) -> bool {
self.kind == CommentKind::MultiLineBlock
}
#[inline]
pub fn is_leading(self) -> bool {
self.position == CommentPosition::Leading
}
#[inline]
pub fn is_trailing(self) -> bool {
self.position == CommentPosition::Trailing
}
#[inline]
pub fn is_normal(self) -> bool {
self.content == CommentContent::None
}
#[inline]
pub fn is_annotation(self) -> bool {
self.content != CommentContent::None
&& self.content != CommentContent::Legal
&& self.content != CommentContent::Jsdoc
&& self.content != CommentContent::JsdocLegal
}
#[inline]
pub fn is_jsdoc(self) -> bool {
matches!(self.content, CommentContent::Jsdoc | CommentContent::JsdocLegal)
&& self.is_leading()
}
#[inline]
pub fn is_legal(self) -> bool {
matches!(self.content, CommentContent::Legal | CommentContent::JsdocLegal)
&& self.is_leading()
}
#[inline]
pub fn is_pure(self) -> bool {
self.content == CommentContent::Pure
}
#[inline]
pub fn is_no_side_effects(self) -> bool {
self.content == CommentContent::NoSideEffects
}
#[inline]
pub fn is_webpack(self) -> bool {
self.content == CommentContent::Webpack
}
#[inline]
pub fn is_turbopack(self) -> bool {
self.content == CommentContent::Turbopack
}
#[inline]
pub fn is_vite(self) -> bool {
self.content == CommentContent::Vite
}
#[inline]
pub fn is_coverage_ignore(self) -> bool {
self.content == CommentContent::CoverageIgnore && self.is_leading()
}
#[inline]
pub fn preceded_by_newline(self) -> bool {
self.newlines.contains(CommentNewlines::Leading)
}
#[inline]
pub fn followed_by_newline(self) -> bool {
self.newlines.contains(CommentNewlines::Trailing)
}
#[inline]
pub fn has_newlines_around(self) -> bool {
self.newlines != CommentNewlines::None
}
#[inline]
pub fn set_preceded_by_newline(&mut self, preceded_by_newline: bool) {
self.newlines.set(CommentNewlines::Leading, preceded_by_newline);
}
#[inline]
pub fn set_followed_by_newline(&mut self, followed_by_newline: bool) {
self.newlines.set(CommentNewlines::Trailing, followed_by_newline);
}
}