use crate::ast::Trivia;
use crate::ast::TriviaKind;
pub mod docblock;
fn byte_lines(input: &[u8]) -> impl Iterator<Item = &[u8]> + '_ {
let mut rest = input;
std::iter::from_fn(move || {
if rest.is_empty() {
return None;
}
match memchr::memchr(b'\n', rest) {
Some(pos) => {
let mut line = &rest[..pos];
if line.last() == Some(&b'\r') {
line = &line[..line.len() - 1];
}
rest = &rest[pos + 1..];
Some(line)
}
None => {
let line = rest;
rest = &[];
Some(line)
}
}
})
}
fn trim_ascii_whitespace_start(input: &[u8]) -> &[u8] {
let start = input.iter().position(|b| !b.is_ascii_whitespace()).unwrap_or(input.len());
&input[start..]
}
#[inline]
#[must_use]
pub fn comment_lines<'arena>(trivia: &Trivia<'arena>) -> Vec<(u32, &'arena [u8])> {
let full_text = trivia.value;
let (content_start_offset, content_end_offset) = match trivia.kind {
TriviaKind::MultiLineComment => (2u32, full_text.len() as u32 - 2),
TriviaKind::DocBlockComment => (3u32, full_text.len() as u32 - 2),
TriviaKind::SingleLineComment => (2u32, full_text.len() as u32),
TriviaKind::HashComment => (1u32, full_text.len() as u32),
TriviaKind::WhiteSpace => return vec![],
};
if content_start_offset >= content_end_offset {
return vec![];
}
let content = &full_text[content_start_offset as usize..content_end_offset as usize];
let mut lines = Vec::new();
for line in byte_lines(content) {
let relative_line_offset = (line.as_ptr() as u32) - (content.as_ptr() as u32);
let offset_in_trivia = content_start_offset + relative_line_offset;
let cleaned_line = if trivia.kind.is_block_comment() {
let trimmed = trim_ascii_whitespace_start(line);
if let Some(stripped) = trimmed.strip_prefix(b"*") { trim_ascii_whitespace_start(stripped) } else { line }
} else {
line
};
let trimmed_bytes = (cleaned_line.as_ptr() as u32) - (line.as_ptr() as u32);
let final_offset = offset_in_trivia + trimmed_bytes;
lines.push((final_offset, cleaned_line));
}
lines
}