use crate::TriviaPiece;
use crate::arc::{Arc, HeaderSlice, ThinArc};
use flash_text_size::TextSize;
use std::fmt::Formatter;
use std::mem::ManuallyDrop;
use std::{fmt, mem, ptr};
#[derive(PartialEq, Eq, Hash)]
pub(crate) struct GreenTriviaHead {
#[cfg(feature = "countme")]
_c: countme::Count<GreenTrivia>,
}
#[cfg(feature = "countme")]
pub(crate) fn has_live() -> bool {
countme::get::<GreenTrivia>().live > 0
}
type ReprThin = HeaderSlice<GreenTriviaHead, [TriviaPiece; 0]>;
#[repr(transparent)]
pub(crate) struct GreenTriviaData {
data: ReprThin,
}
impl GreenTriviaData {
#[expect(unused)]
#[inline]
pub fn header(&self) -> &GreenTriviaHead {
&self.data.header
}
#[inline]
pub fn pieces(&self) -> &[TriviaPiece] {
self.data.slice()
}
#[inline]
pub(crate) fn to_owned(&self) -> GreenTrivia {
unsafe {
let green = GreenTrivia::from_raw(self as *const _ as *mut _);
let green = ManuallyDrop::new(green);
GreenTrivia::clone(&green)
}
}
}
impl PartialEq for GreenTriviaData {
fn eq(&self, other: &Self) -> bool {
self.pieces() == other.pieces()
}
}
impl fmt::Debug for GreenTriviaData {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.debug_list().entries(self.pieces().iter()).finish()
}
}
#[derive(Eq, PartialEq, Hash, Clone)]
#[repr(transparent)]
pub(crate) struct GreenTrivia {
ptr: Option<ThinArc<GreenTriviaHead, TriviaPiece>>,
}
impl fmt::Debug for GreenTrivia {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(self.pieces(), f)
}
}
impl GreenTrivia {
pub fn new<I>(pieces: I) -> Self
where
I: IntoIterator<Item = TriviaPiece>,
I::IntoIter: ExactSizeIterator,
{
let data = ThinArc::from_header_and_iter(
GreenTriviaHead {
#[cfg(feature = "countme")]
_c: countme::Count::new(),
},
pieces.into_iter(),
);
Self { ptr: Some(data) }
}
pub fn empty() -> Self {
Self { ptr: None }
}
pub fn text_len(&self) -> TextSize {
let mut len = TextSize::default();
for piece in self.pieces() {
len += piece.length
}
len
}
pub fn len(&self) -> usize {
match &self.ptr {
None => 0,
Some(ptr) => ptr.len(),
}
}
pub fn pieces(&self) -> &[TriviaPiece] {
match &self.ptr {
None => &[],
Some(ptr) => ptr.slice(),
}
}
pub fn get_piece(&self, index: usize) -> Option<&TriviaPiece> {
self.pieces().get(index)
}
pub(crate) fn into_raw(self) -> *mut GreenTriviaData {
self.ptr.map_or_else(ptr::null_mut, |ptr| {
Arc::from_thin(ptr).into_raw().cast().as_ptr()
})
}
pub(crate) unsafe fn from_raw(ptr: *mut GreenTriviaData) -> Self {
unsafe {
if let Some(ptr) = ptr.as_ref() {
let arc = Arc::from_raw(&ptr.data as *const ReprThin);
let arc =
mem::transmute::<Arc<ReprThin>, ThinArc<GreenTriviaHead, TriviaPiece>>(arc);
Self { ptr: Some(arc) }
} else {
Self { ptr: None }
}
}
}
}
#[cfg(test)]
mod tests {
use crate::TriviaPiece;
use crate::green::trivia::{GreenTrivia, GreenTriviaHead};
use crate::syntax::TriviaPieceKind;
use flash_text_size::TextSize;
impl GreenTrivia {
pub fn whitespace<L: Into<TextSize>>(len: L) -> Self {
Self::single(TriviaPieceKind::Whitespace, len.into())
}
pub fn single_line_comment<L: Into<TextSize>>(len: L) -> Self {
Self::single(TriviaPieceKind::SingleLineComment, len.into())
}
pub fn single<L: Into<TextSize>>(kind: TriviaPieceKind, len: L) -> Self {
Self::new([TriviaPiece::new(kind, len)])
}
}
#[test]
fn sizes() {
assert_eq!(0, std::mem::size_of::<GreenTriviaHead>());
assert_eq!(8, std::mem::size_of::<GreenTrivia>());
}
}